From f06c0532c9d6eb7b30d894bd255ad6aba0762e87 Mon Sep 17 00:00:00 2001 From: Shri Date: Mon, 11 Feb 2019 15:22:50 +0530 Subject: [PATCH 01/38] first commit --- crudj2e/Dockerfile | 2 + crudj2e/README.md | 11 + .../catalina.policy | 260 + .../catalina.properties | 215 + .../context.xml | 29 + .../server.xml | 155 + .../tomcat-users.xml | 40 + .../web.xml | 4705 +++++++++++++++++ crudj2e/pom.xml | 52 + .../java/com/stackroute/dao/MovieDao.java | 22 + .../java/com/stackroute/dao/MovieDaoImpl.java | 139 + .../java/com/stackroute/domain/Movie.java | 46 + .../java/com/stackroute/web/UDController.java | 83 + .../com/stackroute/web/movieController.java | 71 + crudj2e/src/main/webapp/META-INF/context.xml | 30 + crudj2e/src/main/webapp/WEB-INF/web.xml | 27 + crudj2e/src/main/webapp/addMovie.jsp | 47 + crudj2e/src/main/webapp/index.jsp | 16 + crudj2e/src/main/webapp/showMovies.jsp | 53 + crudj2e/src/main/webapp/update.jsp | 52 + 20 files changed, 6055 insertions(+) create mode 100644 crudj2e/Dockerfile create mode 100644 crudj2e/README.md create mode 100644 crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.policy create mode 100644 crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.properties create mode 100644 crudj2e/Servers/Tomcat v9.0 Server at localhost-config/context.xml create mode 100644 crudj2e/Servers/Tomcat v9.0 Server at localhost-config/server.xml create mode 100644 crudj2e/Servers/Tomcat v9.0 Server at localhost-config/tomcat-users.xml create mode 100644 crudj2e/Servers/Tomcat v9.0 Server at localhost-config/web.xml create mode 100644 crudj2e/pom.xml create mode 100644 crudj2e/src/main/java/com/stackroute/dao/MovieDao.java create mode 100644 crudj2e/src/main/java/com/stackroute/dao/MovieDaoImpl.java create mode 100644 crudj2e/src/main/java/com/stackroute/domain/Movie.java create mode 100644 crudj2e/src/main/java/com/stackroute/web/UDController.java create mode 100644 crudj2e/src/main/java/com/stackroute/web/movieController.java create mode 100644 crudj2e/src/main/webapp/META-INF/context.xml create mode 100644 crudj2e/src/main/webapp/WEB-INF/web.xml create mode 100644 crudj2e/src/main/webapp/addMovie.jsp create mode 100644 crudj2e/src/main/webapp/index.jsp create mode 100644 crudj2e/src/main/webapp/showMovies.jsp create mode 100644 crudj2e/src/main/webapp/update.jsp diff --git a/crudj2e/Dockerfile b/crudj2e/Dockerfile new file mode 100644 index 0000000..392e727 --- /dev/null +++ b/crudj2e/Dockerfile @@ -0,0 +1,2 @@ +FROM tomcat:9.0.14-jre11-slim +ADD ./target/CRUDJ2E.war /usr/local/tomcat/webapps/ \ No newline at end of file diff --git a/crudj2e/README.md b/crudj2e/README.md new file mode 100644 index 0000000..8bdf5a6 --- /dev/null +++ b/crudj2e/README.md @@ -0,0 +1,11 @@ +Application Name: CRUDJ2E + +This is a Java Enterprise Edition(J2E) project built using maven. +Sys-Cop as a product must be able to monitor applications in various environments. The main purpose of this project is to provide a one of the many test environments for our product. + +Java version: 11 +Database used: MYSQL +Landing page url: localhost:8080/CRUDJ2E +Port exposed: 8080 +Dockerised?: Yes. + diff --git a/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.policy b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.policy new file mode 100644 index 0000000..34cfe2d --- /dev/null +++ b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.policy @@ -0,0 +1,260 @@ +// 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. + +// ============================================================================ +// catalina.policy - Security Policy Permissions for Tomcat +// +// This file contains a default set of security policies to be enforced (by the +// JVM) when Catalina is executed with the "-security" option. In addition +// to the permissions granted here, the following additional permissions are +// granted to each web application: +// +// * Read access to the web application's document root directory +// * Read, write and delete access to the web application's working directory +// ============================================================================ + + +// ========== SYSTEM CODE PERMISSIONS ========================================= + + +// These permissions apply to javac +grant codeBase "file:${java.home}/lib/-" { + permission java.security.AllPermission; +}; + +// These permissions apply to all shared system extensions +grant codeBase "file:${java.home}/jre/lib/ext/-" { + permission java.security.AllPermission; +}; + +// These permissions apply to javac when ${java.home] points at $JAVA_HOME/jre +grant codeBase "file:${java.home}/../lib/-" { + permission java.security.AllPermission; +}; + +// These permissions apply to all shared system extensions when +// ${java.home} points at $JAVA_HOME/jre +grant codeBase "file:${java.home}/lib/ext/-" { + permission java.security.AllPermission; +}; + + +// ========== CATALINA CODE PERMISSIONS ======================================= + + +// These permissions apply to the daemon code +grant codeBase "file:${catalina.home}/bin/commons-daemon.jar" { + permission java.security.AllPermission; +}; + +// These permissions apply to the logging API +// Note: If tomcat-juli.jar is in ${catalina.base} and not in ${catalina.home}, +// update this section accordingly. +// grant codeBase "file:${catalina.base}/bin/tomcat-juli.jar" {..} +grant codeBase "file:${catalina.home}/bin/tomcat-juli.jar" { + permission java.io.FilePermission + "${java.home}${file.separator}lib${file.separator}logging.properties", "read"; + + permission java.io.FilePermission + "${catalina.base}${file.separator}conf${file.separator}logging.properties", "read"; + permission java.io.FilePermission + "${catalina.base}${file.separator}logs", "read, write"; + permission java.io.FilePermission + "${catalina.base}${file.separator}logs${file.separator}*", "read, write, delete"; + + permission java.lang.RuntimePermission "shutdownHooks"; + permission java.lang.RuntimePermission "getClassLoader"; + permission java.lang.RuntimePermission "setContextClassLoader"; + + permission java.lang.management.ManagementPermission "monitor"; + + permission java.util.logging.LoggingPermission "control"; + + permission java.util.PropertyPermission "java.util.logging.config.class", "read"; + permission java.util.PropertyPermission "java.util.logging.config.file", "read"; + permission java.util.PropertyPermission "org.apache.juli.AsyncLoggerPollInterval", "read"; + permission java.util.PropertyPermission "org.apache.juli.AsyncMaxRecordCount", "read"; + permission java.util.PropertyPermission "org.apache.juli.AsyncOverflowDropType", "read"; + permission java.util.PropertyPermission "org.apache.juli.ClassLoaderLogManager.debug", "read"; + permission java.util.PropertyPermission "catalina.base", "read"; + + // Note: To enable per context logging configuration, permit read access to + // the appropriate file. Be sure that the logging configuration is + // secure before enabling such access. + // E.g. for the examples web application (uncomment and unwrap + // the following to be on a single line): + // permission java.io.FilePermission "${catalina.base}${file.separator} + // webapps${file.separator}examples${file.separator}WEB-INF + // ${file.separator}classes${file.separator}logging.properties", "read"; +}; + +// These permissions apply to the server startup code +grant codeBase "file:${catalina.home}/bin/bootstrap.jar" { + permission java.security.AllPermission; +}; + +// These permissions apply to the servlet API classes +// and those that are shared across all class loaders +// located in the "lib" directory +grant codeBase "file:${catalina.home}/lib/-" { + permission java.security.AllPermission; +}; + + +// If using a per instance lib directory, i.e. ${catalina.base}/lib, +// then the following permission will need to be uncommented +// grant codeBase "file:${catalina.base}/lib/-" { +// permission java.security.AllPermission; +// }; + + +// ========== WEB APPLICATION PERMISSIONS ===================================== + + +// These permissions are granted by default to all web applications +// In addition, a web application will be given a read FilePermission +// for all files and directories in its document root. +grant { + // Required for JNDI lookup of named JDBC DataSource's and + // javamail named MimePart DataSource used to send mail + permission java.util.PropertyPermission "java.home", "read"; + permission java.util.PropertyPermission "java.naming.*", "read"; + permission java.util.PropertyPermission "javax.sql.*", "read"; + + // OS Specific properties to allow read access + permission java.util.PropertyPermission "os.name", "read"; + permission java.util.PropertyPermission "os.version", "read"; + permission java.util.PropertyPermission "os.arch", "read"; + permission java.util.PropertyPermission "file.separator", "read"; + permission java.util.PropertyPermission "path.separator", "read"; + permission java.util.PropertyPermission "line.separator", "read"; + + // JVM properties to allow read access + permission java.util.PropertyPermission "java.version", "read"; + permission java.util.PropertyPermission "java.vendor", "read"; + permission java.util.PropertyPermission "java.vendor.url", "read"; + permission java.util.PropertyPermission "java.class.version", "read"; + permission java.util.PropertyPermission "java.specification.version", "read"; + permission java.util.PropertyPermission "java.specification.vendor", "read"; + permission java.util.PropertyPermission "java.specification.name", "read"; + + permission java.util.PropertyPermission "java.vm.specification.version", "read"; + permission java.util.PropertyPermission "java.vm.specification.vendor", "read"; + permission java.util.PropertyPermission "java.vm.specification.name", "read"; + permission java.util.PropertyPermission "java.vm.version", "read"; + permission java.util.PropertyPermission "java.vm.vendor", "read"; + permission java.util.PropertyPermission "java.vm.name", "read"; + + // Required for OpenJMX + permission java.lang.RuntimePermission "getAttribute"; + + // Allow read of JAXP compliant XML parser debug + permission java.util.PropertyPermission "jaxp.debug", "read"; + + // All JSPs need to be able to read this package + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat"; + + // Precompiled JSPs need access to these packages. + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.el"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.jasper.runtime"; + permission java.lang.RuntimePermission + "accessClassInPackage.org.apache.jasper.runtime.*"; + + // Applications using WebSocket need to be able to access these packages + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.websocket"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.tomcat.websocket.server"; +}; + + +// The Manager application needs access to the following packages to support the +// session display functionality. It also requires the custom Tomcat +// DeployXmlPermission to enable the use of META-INF/context.xml +// These settings support the following configurations: +// - default CATALINA_HOME == CATALINA_BASE +// - CATALINA_HOME != CATALINA_BASE, per instance Manager in CATALINA_BASE +// - CATALINA_HOME != CATALINA_BASE, shared Manager in CATALINA_HOME +grant codeBase "file:${catalina.base}/webapps/manager/-" { + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.util"; + permission org.apache.catalina.security.DeployXmlPermission "manager"; +}; +grant codeBase "file:${catalina.home}/webapps/manager/-" { + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager.util"; + permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.util"; + permission org.apache.catalina.security.DeployXmlPermission "manager"; +}; + +// The Host Manager application needs the custom Tomcat DeployXmlPermission to +// enable the use of META-INF/context.xml +// These settings support the following configurations: +// - default CATALINA_HOME == CATALINA_BASE +// - CATALINA_HOME != CATALINA_BASE, per instance Host Manager in CATALINA_BASE +// - CATALINA_HOME != CATALINA_BASE, shared Host Manager in CATALINA_HOME +grant codeBase "file:${catalina.base}/webapps/host-manager/-" { + permission org.apache.catalina.security.DeployXmlPermission "host-manager"; +}; +grant codeBase "file:${catalina.home}/webapps/host-manager/-" { + permission org.apache.catalina.security.DeployXmlPermission "host-manager"; +}; + + +// You can assign additional permissions to particular web applications by +// adding additional "grant" entries here, based on the code base for that +// application, /WEB-INF/classes/, or /WEB-INF/lib/ jar files. +// +// Different permissions can be granted to JSP pages, classes loaded from +// the /WEB-INF/classes/ directory, all jar files in the /WEB-INF/lib/ +// directory, or even to individual jar files in the /WEB-INF/lib/ directory. +// +// For instance, assume that the standard "examples" application +// included a JDBC driver that needed to establish a network connection to the +// corresponding database and used the scrape taglib to get the weather from +// the NOAA web server. You might create a "grant" entries like this: +// +// The permissions granted to the context root directory apply to JSP pages. +// grant codeBase "file:${catalina.base}/webapps/examples/-" { +// permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect"; +// permission java.net.SocketPermission "*.noaa.gov:80", "connect"; +// }; +// +// The permissions granted to the context WEB-INF/classes directory +// grant codeBase "file:${catalina.base}/webapps/examples/WEB-INF/classes/-" { +// }; +// +// The permission granted to your JDBC driver +// grant codeBase "jar:file:${catalina.base}/webapps/examples/WEB-INF/lib/driver.jar!/-" { +// permission java.net.SocketPermission "dbhost.mycompany.com:5432", "connect"; +// }; +// The permission granted to the scrape taglib +// grant codeBase "jar:file:${catalina.base}/webapps/examples/WEB-INF/lib/scrape.jar!/-" { +// permission java.net.SocketPermission "*.noaa.gov:80", "connect"; +// }; + +// To grant permissions for web applications using packed WAR files, use the +// Tomcat specific WAR url scheme. +// +// The permissions granted to the entire web application +// grant codeBase "war:file:${catalina.base}/webapps/examples.war*/-" { +// }; +// +// The permissions granted to a specific JAR +// grant codeBase "war:file:${catalina.base}/webapps/examples.war*/WEB-INF/lib/foo.jar" { +// }; diff --git a/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.properties b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.properties new file mode 100644 index 0000000..b77b09f --- /dev/null +++ b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/catalina.properties @@ -0,0 +1,215 @@ +# 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. + +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageAccess unless the +# corresponding RuntimePermission ("accessClassInPackage."+package) has +# been granted. +package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,org.apache.tomcat. +# +# List of comma-separated packages that start with or equal this string +# will cause a security exception to be thrown when +# passed to checkPackageDefinition unless the +# corresponding RuntimePermission ("defineClassInPackage."+package) has +# been granted. +# +# by default, no packages are restricted for definition, and none of +# the class loaders supplied with the JDK call checkPackageDefinition. +# +package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,\ +org.apache.jasper.,org.apache.naming.,org.apache.tomcat. + +# +# +# List of comma-separated paths defining the contents of the "common" +# classloader. Prefixes should be used to define what is the repository type. +# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. +# If left as blank,the JVM system loader will be used as Catalina's "common" +# loader. +# Examples: +# "foo": Add this folder as a class repository +# "foo/*.jar": Add all the JARs of the specified folder as class +# repositories +# "foo/bar.jar": Add bar.jar as a class repository +# +# Note: Values are enclosed in double quotes ("...") in case either the +# ${catalina.base} path or the ${catalina.home} path contains a comma. +# Because double quotes are used for quoting, the double quote character +# may not appear in a path. +common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" + +# +# List of comma-separated paths defining the contents of the "server" +# classloader. Prefixes should be used to define what is the repository type. +# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute. +# If left as blank, the "common" loader will be used as Catalina's "server" +# loader. +# Examples: +# "foo": Add this folder as a class repository +# "foo/*.jar": Add all the JARs of the specified folder as class +# repositories +# "foo/bar.jar": Add bar.jar as a class repository +# +# Note: Values may be enclosed in double quotes ("...") in case either the +# ${catalina.base} path or the ${catalina.home} path contains a comma. +# Because double quotes are used for quoting, the double quote character +# may not appear in a path. +server.loader= + +# +# List of comma-separated paths defining the contents of the "shared" +# classloader. Prefixes should be used to define what is the repository type. +# Path may be relative to the CATALINA_BASE path or absolute. If left as blank, +# the "common" loader will be used as Catalina's "shared" loader. +# Examples: +# "foo": Add this folder as a class repository +# "foo/*.jar": Add all the JARs of the specified folder as class +# repositories +# "foo/bar.jar": Add bar.jar as a class repository +# Please note that for single jars, e.g. bar.jar, you need the URL form +# starting with file:. +# +# Note: Values may be enclosed in double quotes ("...") in case either the +# ${catalina.base} path or the ${catalina.home} path contains a comma. +# Because double quotes are used for quoting, the double quote character +# may not appear in a path. +shared.loader= + +# Default list of JAR files that should not be scanned using the JarScanner +# functionality. This is typically used to scan JARs for configuration +# information. JARs that do not contain such information may be excluded from +# the scan to speed up the scanning process. This is the default list. JARs on +# this list are excluded from all scans. The list must be a comma separated list +# of JAR file names. +# The list of JARs to skip may be over-ridden at a Context level for individual +# scan types by configuring a JarScanner with a nested JarScanFilter. +# The JARs listed below include: +# - Tomcat Bootstrap JARs +# - Tomcat API JARs +# - Catalina JARs +# - Jasper JARs +# - Tomcat JARs +# - Common non-Tomcat JARs +# - Test JARs (JUnit, Cobertura and dependencies) +tomcat.util.scan.StandardJarScanFilter.jarsToSkip=\ +annotations-api.jar,\ +ant-junit*.jar,\ +ant-launcher.jar,\ +ant.jar,\ +asm-*.jar,\ +aspectj*.jar,\ +bootstrap.jar,\ +catalina-ant.jar,\ +catalina-ha.jar,\ +catalina-jmx-remote.jar,\ +catalina-storeconfig.jar,\ +catalina-tribes.jar,\ +catalina-ws.jar,\ +catalina.jar,\ +cglib-*.jar,\ +cobertura-*.jar,\ +commons-beanutils*.jar,\ +commons-codec*.jar,\ +commons-collections*.jar,\ +commons-daemon.jar,\ +commons-dbcp*.jar,\ +commons-digester*.jar,\ +commons-fileupload*.jar,\ +commons-httpclient*.jar,\ +commons-io*.jar,\ +commons-lang*.jar,\ +commons-logging*.jar,\ +commons-math*.jar,\ +commons-pool*.jar,\ +dom4j-*.jar,\ +easymock-*.jar,\ +ecj-*.jar,\ +el-api.jar,\ +geronimo-spec-jaxrpc*.jar,\ +h2*.jar,\ +hamcrest-*.jar,\ +hibernate*.jar,\ +httpclient*.jar,\ +icu4j-*.jar,\ +jasper-el.jar,\ +jasper.jar,\ +jaspic-api.jar,\ +jaxb-*.jar,\ +jaxen-*.jar,\ +jdom-*.jar,\ +jetty-*.jar,\ +jmx-tools.jar,\ +jmx.jar,\ +jsp-api.jar,\ +jstl.jar,\ +jta*.jar,\ +junit-*.jar,\ +junit.jar,\ +log4j*.jar,\ +mail*.jar,\ +objenesis-*.jar,\ +oraclepki.jar,\ +oro-*.jar,\ +servlet-api-*.jar,\ +servlet-api.jar,\ +slf4j*.jar,\ +taglibs-standard-spec-*.jar,\ +tagsoup-*.jar,\ +tomcat-api.jar,\ +tomcat-coyote.jar,\ +tomcat-dbcp.jar,\ +tomcat-i18n-de.jar,\ +tomcat-i18n-en.jar,\ +tomcat-i18n-es.jar,\ +tomcat-i18n-fr.jar,\ +tomcat-i18n-ja.jar,\ +tomcat-i18n-ko.jar,\ +tomcat-i18n-pt-BR.jar,\ +tomcat-i18n-ru.jar,\ +tomcat-i18n-zh-CN.jar,\ +tomcat-jdbc.jar,\ +tomcat-jni.jar,\ +tomcat-juli-adapters.jar,\ +tomcat-juli.jar,\ +tomcat-util-scan.jar,\ +tomcat-util.jar,\ +tomcat-websocket.jar,\ +tools.jar,\ +websocket-api.jar,\ +wsdl4j*.jar,\ +xercesImpl.jar,\ +xml-apis.jar,\ +xmlParserAPIs-*.jar,\ +xmlParserAPIs.jar,\ +xom-*.jar + +# Default list of JAR files that should be scanned that overrides the default +# jarsToSkip list above. This is typically used to include a specific JAR that +# has been excluded by a broad file name pattern in the jarsToSkip list. +# The list of JARs to scan may be over-ridden at a Context level for individual +# scan types by configuring a JarScanner with a nested JarScanFilter. +tomcat.util.scan.StandardJarScanFilter.jarsToScan=\ +log4j-taglib*.jar,\ +log4j-web*.jar,\ +log4javascript*.jar,\ +slf4j-taglib*.jar + +# String cache configuration. +tomcat.util.buf.StringCache.byte.enabled=true +#tomcat.util.buf.StringCache.char.enabled=true +#tomcat.util.buf.StringCache.trainThreshold=500000 +#tomcat.util.buf.StringCache.cacheSize=5000 diff --git a/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/context.xml b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/context.xml new file mode 100644 index 0000000..befa0c2 --- /dev/null +++ b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/context.xml @@ -0,0 +1,29 @@ + + + + + + WEB-INF/web.xml + WEB-INF/tomcat-web.xml + ${catalina.base}/conf/web.xml + + + + \ No newline at end of file diff --git a/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/server.xml b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/server.xml new file mode 100644 index 0000000..63e6735 --- /dev/null +++ b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/server.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/tomcat-users.xml b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/tomcat-users.xml new file mode 100644 index 0000000..2e175df --- /dev/null +++ b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/tomcat-users.xml @@ -0,0 +1,40 @@ + + + + + + \ No newline at end of file diff --git a/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/web.xml b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/web.xml new file mode 100644 index 0000000..3806627 --- /dev/null +++ b/crudj2e/Servers/Tomcat v9.0 Server at localhost-config/web.xml @@ -0,0 +1,4705 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + default + org.apache.catalina.servlets.DefaultServlet + + debug + 0 + + + listings + false + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jsp + org.apache.jasper.servlet.JspServlet + + fork + false + + + xpoweredBy + false + + 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + default + / + + + + + jsp + *.jsp + *.jspx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 30 + + + + + + + + + + + + + 123 + application/vnd.lotus-1-2-3 + + + 3dml + text/vnd.in3d.3dml + + + 3ds + image/x-3ds + + + 3g2 + video/3gpp2 + + + 3gp + video/3gpp + + + 7z + application/x-7z-compressed + + + aab + application/x-authorware-bin + + + aac + audio/x-aac + + + aam + application/x-authorware-map + + + aas + application/x-authorware-seg + + + abs + audio/x-mpeg + + + abw + application/x-abiword + + + ac + application/pkix-attr-cert + + + acc + application/vnd.americandynamics.acc + + + ace + application/x-ace-compressed + + + acu + application/vnd.acucobol + + + acutc + application/vnd.acucorp + + + adp + audio/adpcm + + + aep + application/vnd.audiograph + + + afm + application/x-font-type1 + + + afp + application/vnd.ibm.modcap + + + ahead + application/vnd.ahead.space + + + ai + application/postscript + + + aif + audio/x-aiff + + + aifc + audio/x-aiff + + + aiff + audio/x-aiff + + + aim + application/x-aim + + + air + application/vnd.adobe.air-application-installer-package+zip + + + ait + application/vnd.dvb.ait + + + ami + application/vnd.amiga.ami + + + anx + application/annodex + + + apk + application/vnd.android.package-archive + + + appcache + text/cache-manifest + + + application + application/x-ms-application + + + apr + application/vnd.lotus-approach + + + arc + application/x-freearc + + + art + image/x-jg + + + asc + application/pgp-signature + + + asf + video/x-ms-asf + + + asm + text/x-asm + + + aso + application/vnd.accpac.simply.aso + + + asx + video/x-ms-asf + + + atc + application/vnd.acucorp + + + atom + application/atom+xml + + + atomcat + application/atomcat+xml + + + atomsvc + application/atomsvc+xml + + + atx + application/vnd.antix.game-component + + + au + audio/basic + + + avi + video/x-msvideo + + + avx + video/x-rad-screenplay + + + aw + application/applixware + + + axa + audio/annodex + + + axv + video/annodex + + + azf + application/vnd.airzip.filesecure.azf + + + azs + application/vnd.airzip.filesecure.azs + + + azw + application/vnd.amazon.ebook + + + bat + application/x-msdownload + + + bcpio + application/x-bcpio + + + bdf + application/x-font-bdf + + + bdm + application/vnd.syncml.dm+wbxml + + + bed + application/vnd.realvnc.bed + + + bh2 + application/vnd.fujitsu.oasysprs + + + bin + application/octet-stream + + + blb + application/x-blorb + + + blorb + application/x-blorb + + + bmi + application/vnd.bmi + + + bmp + image/bmp + + + body + text/html + + + book + application/vnd.framemaker + + + box + application/vnd.previewsystems.box + + + boz + application/x-bzip2 + + + bpk + application/octet-stream + + + btif + image/prs.btif + + + bz + application/x-bzip + + + bz2 + application/x-bzip2 + + + c + text/x-c + + + c11amc + application/vnd.cluetrust.cartomobile-config + + + c11amz + application/vnd.cluetrust.cartomobile-config-pkg + + + c4d + application/vnd.clonk.c4group + + + c4f + application/vnd.clonk.c4group + + + c4g + application/vnd.clonk.c4group + + + c4p + application/vnd.clonk.c4group + + + c4u + application/vnd.clonk.c4group + + + cab + application/vnd.ms-cab-compressed + + + caf + audio/x-caf + + + cap + application/vnd.tcpdump.pcap + + + car + application/vnd.curl.car + + + cat + application/vnd.ms-pki.seccat + + + cb7 + application/x-cbr + + + cba + application/x-cbr + + + cbr + application/x-cbr + + + cbt + application/x-cbr + + + cbz + application/x-cbr + + + cc + text/x-c + + + cct + application/x-director + + + ccxml + application/ccxml+xml + + + cdbcmsg + application/vnd.contact.cmsg + + + cdf + application/x-cdf + + + cdkey + application/vnd.mediastation.cdkey + + + cdmia + application/cdmi-capability + + + cdmic + application/cdmi-container + + + cdmid + application/cdmi-domain + + + cdmio + application/cdmi-object + + + cdmiq + application/cdmi-queue + + + cdx + chemical/x-cdx + + + cdxml + application/vnd.chemdraw+xml + + + cdy + application/vnd.cinderella + + + cer + application/pkix-cert + + + cfs + application/x-cfs-compressed + + + cgm + image/cgm + + + chat + application/x-chat + + + chm + application/vnd.ms-htmlhelp + + + chrt + application/vnd.kde.kchart + + + cif + chemical/x-cif + + + cii + application/vnd.anser-web-certificate-issue-initiation + + + cil + application/vnd.ms-artgalry + + + cla + application/vnd.claymore + + + class + application/java + + + clkk + application/vnd.crick.clicker.keyboard + + + clkp + application/vnd.crick.clicker.palette + + + clkt + application/vnd.crick.clicker.template + + + clkw + application/vnd.crick.clicker.wordbank + + + clkx + application/vnd.crick.clicker + + + clp + application/x-msclip + + + cmc + application/vnd.cosmocaller + + + cmdf + chemical/x-cmdf + + + cml + chemical/x-cml + + + cmp + application/vnd.yellowriver-custom-menu + + + cmx + image/x-cmx + + + cod + application/vnd.rim.cod + + + com + application/x-msdownload + + + conf + text/plain + + + cpio + application/x-cpio + + + cpp + text/x-c + + + cpt + application/mac-compactpro + + + crd + application/x-mscardfile + + + crl + application/pkix-crl + + + crt + application/x-x509-ca-cert + + + cryptonote + application/vnd.rig.cryptonote + + + csh + application/x-csh + + + csml + chemical/x-csml + + + csp + application/vnd.commonspace + + + css + text/css + + + cst + application/x-director + + + csv + text/csv + + + cu + application/cu-seeme + + + curl + text/vnd.curl + + + cww + application/prs.cww + + + cxt + application/x-director + + + cxx + text/x-c + + + dae + model/vnd.collada+xml + + + daf + application/vnd.mobius.daf + + + dart + application/vnd.dart + + + dataless + application/vnd.fdsn.seed + + + davmount + application/davmount+xml + + + dbk + application/docbook+xml + + + dcr + application/x-director + + + dcurl + text/vnd.curl.dcurl + + + dd2 + application/vnd.oma.dd2+xml + + + ddd + application/vnd.fujixerox.ddd + + + deb + application/x-debian-package + + + def + text/plain + + + deploy + application/octet-stream + + + der + application/x-x509-ca-cert + + + dfac + application/vnd.dreamfactory + + + dgc + application/x-dgc-compressed + + + dib + image/bmp + + + dic + text/x-c + + + dir + application/x-director + + + dis + application/vnd.mobius.dis + + + dist + application/octet-stream + + + distz + application/octet-stream + + + djv + image/vnd.djvu + + + djvu + image/vnd.djvu + + + dll + application/x-msdownload + + + dmg + application/x-apple-diskimage + + + dmp + application/vnd.tcpdump.pcap + + + dms + application/octet-stream + + + dna + application/vnd.dna + + + doc + application/msword + + + docm + application/vnd.ms-word.document.macroenabled.12 + + + docx + application/vnd.openxmlformats-officedocument.wordprocessingml.document + + + dot + application/msword + + + dotm + application/vnd.ms-word.template.macroenabled.12 + + + dotx + application/vnd.openxmlformats-officedocument.wordprocessingml.template + + + dp + application/vnd.osgi.dp + + + dpg + application/vnd.dpgraph + + + dra + audio/vnd.dra + + + dsc + text/prs.lines.tag + + + dssc + application/dssc+der + + + dtb + application/x-dtbook+xml + + + dtd + application/xml-dtd + + + dts + audio/vnd.dts + + + dtshd + audio/vnd.dts.hd + + + dump + application/octet-stream + + + dv + video/x-dv + + + dvb + video/vnd.dvb.file + + + dvi + application/x-dvi + + + dwf + model/vnd.dwf + + + dwg + image/vnd.dwg + + + dxf + image/vnd.dxf + + + dxp + application/vnd.spotfire.dxp + + + dxr + application/x-director + + + ecelp4800 + audio/vnd.nuera.ecelp4800 + + + ecelp7470 + audio/vnd.nuera.ecelp7470 + + + ecelp9600 + audio/vnd.nuera.ecelp9600 + + + ecma + application/ecmascript + + + edm + application/vnd.novadigm.edm + + + edx + application/vnd.novadigm.edx + + + efif + application/vnd.picsel + + + ei6 + application/vnd.pg.osasli + + + elc + application/octet-stream + + + emf + application/x-msmetafile + + + eml + message/rfc822 + + + emma + application/emma+xml + + + emz + application/x-msmetafile + + + eol + audio/vnd.digital-winds + + + eot + application/vnd.ms-fontobject + + + eps + application/postscript + + + epub + application/epub+zip + + + es3 + application/vnd.eszigno3+xml + + + esa + application/vnd.osgi.subsystem + + + esf + application/vnd.epson.esf + + + et3 + application/vnd.eszigno3+xml + + + etx + text/x-setext + + + eva + application/x-eva + + + evy + application/x-envoy + + + exe + application/octet-stream + + + exi + application/exi + + + ext + application/vnd.novadigm.ext + + + ez + application/andrew-inset + + + ez2 + application/vnd.ezpix-album + + + ez3 + application/vnd.ezpix-package + + + f + text/x-fortran + + + f4v + video/x-f4v + + + f77 + text/x-fortran + + + f90 + text/x-fortran + + + fbs + image/vnd.fastbidsheet + + + fcdt + application/vnd.adobe.formscentral.fcdt + + + fcs + application/vnd.isac.fcs + + + fdf + application/vnd.fdf + + + fe_launch + application/vnd.denovo.fcselayout-link + + + fg5 + application/vnd.fujitsu.oasysgp + + + fgd + application/x-director + + + fh + image/x-freehand + + + fh4 + image/x-freehand + + + fh5 + image/x-freehand + + + fh7 + image/x-freehand + + + fhc + image/x-freehand + + + fig + application/x-xfig + + + flac + audio/flac + + + fli + video/x-fli + + + flo + application/vnd.micrografx.flo + + + flv + video/x-flv + + + flw + application/vnd.kde.kivio + + + flx + text/vnd.fmi.flexstor + + + fly + text/vnd.fly + + + fm + application/vnd.framemaker + + + fnc + application/vnd.frogans.fnc + + + for + text/x-fortran + + + fpx + image/vnd.fpx + + + frame + application/vnd.framemaker + + + fsc + application/vnd.fsc.weblaunch + + + fst + image/vnd.fst + + + ftc + application/vnd.fluxtime.clip + + + fti + application/vnd.anser-web-funds-transfer-initiation + + + fvt + video/vnd.fvt + + + fxp + application/vnd.adobe.fxp + + + fxpl + application/vnd.adobe.fxp + + + fzs + application/vnd.fuzzysheet + + + g2w + application/vnd.geoplan + + + g3 + image/g3fax + + + g3w + application/vnd.geospace + + + gac + application/vnd.groove-account + + + gam + application/x-tads + + + gbr + application/rpki-ghostbusters + + + gca + application/x-gca-compressed + + + gdl + model/vnd.gdl + + + geo + application/vnd.dynageo + + + gex + application/vnd.geometry-explorer + + + ggb + application/vnd.geogebra.file + + + ggt + application/vnd.geogebra.tool + + + ghf + application/vnd.groove-help + + + gif + image/gif + + + gim + application/vnd.groove-identity-message + + + gml + application/gml+xml + + + gmx + application/vnd.gmx + + + gnumeric + application/x-gnumeric + + + gph + application/vnd.flographit + + + gpx + application/gpx+xml + + + gqf + application/vnd.grafeq + + + gqs + application/vnd.grafeq + + + gram + application/srgs + + + gramps + application/x-gramps-xml + + + gre + application/vnd.geometry-explorer + + + grv + application/vnd.groove-injector + + + grxml + application/srgs+xml + + + gsf + application/x-font-ghostscript + + + gtar + application/x-gtar + + + gtm + application/vnd.groove-tool-message + + + gtw + model/vnd.gtw + + + gv + text/vnd.graphviz + + + gxf + application/gxf + + + gxt + application/vnd.geonext + + + gz + application/x-gzip + + + h + text/x-c + + + h261 + video/h261 + + + h263 + video/h263 + + + h264 + video/h264 + + + hal + application/vnd.hal+xml + + + hbci + application/vnd.hbci + + + hdf + application/x-hdf + + + hh + text/x-c + + + hlp + application/winhlp + + + hpgl + application/vnd.hp-hpgl + + + hpid + application/vnd.hp-hpid + + + hps + application/vnd.hp-hps + + + hqx + application/mac-binhex40 + + + htc + text/x-component + + + htke + application/vnd.kenameaapp + + + htm + text/html + + + html + text/html + + + hvd + application/vnd.yamaha.hv-dic + + + hvp + application/vnd.yamaha.hv-voice + + + hvs + application/vnd.yamaha.hv-script + + + i2g + application/vnd.intergeo + + + icc + application/vnd.iccprofile + + + ice + x-conference/x-cooltalk + + + icm + application/vnd.iccprofile + + + ico + image/x-icon + + + ics + text/calendar + + + ief + image/ief + + + ifb + text/calendar + + + ifm + application/vnd.shana.informed.formdata + + + iges + model/iges + + + igl + application/vnd.igloader + + + igm + application/vnd.insors.igm + + + igs + model/iges + + + igx + application/vnd.micrografx.igx + + + iif + application/vnd.shana.informed.interchange + + + imp + application/vnd.accpac.simply.imp + + + ims + application/vnd.ms-ims + + + in + text/plain + + + ink + application/inkml+xml + + + inkml + application/inkml+xml + + + install + application/x-install-instructions + + + iota + application/vnd.astraea-software.iota + + + ipfix + application/ipfix + + + ipk + application/vnd.shana.informed.package + + + irm + application/vnd.ibm.rights-management + + + irp + application/vnd.irepository.package+xml + + + iso + application/x-iso9660-image + + + itp + application/vnd.shana.informed.formtemplate + + + ivp + application/vnd.immervision-ivp + + + ivu + application/vnd.immervision-ivu + + + jad + text/vnd.sun.j2me.app-descriptor + + + jam + application/vnd.jam + + + jar + application/java-archive + + + java + text/x-java-source + + + jisp + application/vnd.jisp + + + jlt + application/vnd.hp-jlyt + + + jnlp + application/x-java-jnlp-file + + + joda + application/vnd.joost.joda-archive + + + jpe + image/jpeg + + + jpeg + image/jpeg + + + jpg + image/jpeg + + + jpgm + video/jpm + + + jpgv + video/jpeg + + + jpm + video/jpm + + + js + application/javascript + + + jsf + text/plain + + + json + application/json + + + jsonml + application/jsonml+json + + + jspf + text/plain + + + kar + audio/midi + + + karbon + application/vnd.kde.karbon + + + kfo + application/vnd.kde.kformula + + + kia + application/vnd.kidspiration + + + kml + application/vnd.google-earth.kml+xml + + + kmz + application/vnd.google-earth.kmz + + + kne + application/vnd.kinar + + + knp + application/vnd.kinar + + + kon + application/vnd.kde.kontour + + + kpr + application/vnd.kde.kpresenter + + + kpt + application/vnd.kde.kpresenter + + + kpxx + application/vnd.ds-keypoint + + + ksp + application/vnd.kde.kspread + + + ktr + application/vnd.kahootz + + + ktx + image/ktx + + + ktz + application/vnd.kahootz + + + kwd + application/vnd.kde.kword + + + kwt + application/vnd.kde.kword + + + lasxml + application/vnd.las.las+xml + + + latex + application/x-latex + + + lbd + application/vnd.llamagraphics.life-balance.desktop + + + lbe + application/vnd.llamagraphics.life-balance.exchange+xml + + + les + application/vnd.hhe.lesson-player + + + lha + application/x-lzh-compressed + + + link66 + application/vnd.route66.link66+xml + + + list + text/plain + + + list3820 + application/vnd.ibm.modcap + + + listafp + application/vnd.ibm.modcap + + + lnk + application/x-ms-shortcut + + + log + text/plain + + + lostxml + application/lost+xml + + + lrf + application/octet-stream + + + lrm + application/vnd.ms-lrm + + + ltf + application/vnd.frogans.ltf + + + lvp + audio/vnd.lucent.voice + + + lwp + application/vnd.lotus-wordpro + + + lzh + application/x-lzh-compressed + + + m13 + application/x-msmediaview + + + m14 + application/x-msmediaview + + + m1v + video/mpeg + + + m21 + application/mp21 + + + m2a + audio/mpeg + + + m2v + video/mpeg + + + m3a + audio/mpeg + + + m3u + audio/x-mpegurl + + + m3u8 + application/vnd.apple.mpegurl + + + m4a + audio/mp4 + + + m4b + audio/mp4 + + + m4r + audio/mp4 + + + m4u + video/vnd.mpegurl + + + m4v + video/mp4 + + + ma + application/mathematica + + + mac + image/x-macpaint + + + mads + application/mads+xml + + + mag + application/vnd.ecowin.chart + + + maker + application/vnd.framemaker + + + man + text/troff + + + mar + application/octet-stream + + + mathml + application/mathml+xml + + + mb + application/mathematica + + + mbk + application/vnd.mobius.mbk + + + mbox + application/mbox + + + mc1 + application/vnd.medcalcdata + + + mcd + application/vnd.mcd + + + mcurl + text/vnd.curl.mcurl + + + mdb + application/x-msaccess + + + mdi + image/vnd.ms-modi + + + me + text/troff + + + mesh + model/mesh + + + meta4 + application/metalink4+xml + + + metalink + application/metalink+xml + + + mets + application/mets+xml + + + mfm + application/vnd.mfmp + + + mft + application/rpki-manifest + + + mgp + application/vnd.osgeo.mapguide.package + + + mgz + application/vnd.proteus.magazine + + + mid + audio/midi + + + midi + audio/midi + + + mie + application/x-mie + + + mif + application/x-mif + + + mime + message/rfc822 + + + mj2 + video/mj2 + + + mjp2 + video/mj2 + + + mk3d + video/x-matroska + + + mka + audio/x-matroska + + + mks + video/x-matroska + + + mkv + video/x-matroska + + + mlp + application/vnd.dolby.mlp + + + mmd + application/vnd.chipnuts.karaoke-mmd + + + mmf + application/vnd.smaf + + + mmr + image/vnd.fujixerox.edmics-mmr + + + mng + video/x-mng + + + mny + application/x-msmoney + + + mobi + application/x-mobipocket-ebook + + + mods + application/mods+xml + + + mov + video/quicktime + + + movie + video/x-sgi-movie + + + mp1 + audio/mpeg + + + mp2 + audio/mpeg + + + mp21 + application/mp21 + + + mp2a + audio/mpeg + + + mp3 + audio/mpeg + + + mp4 + video/mp4 + + + mp4a + audio/mp4 + + + mp4s + application/mp4 + + + mp4v + video/mp4 + + + mpa + audio/mpeg + + + mpc + application/vnd.mophun.certificate + + + mpe + video/mpeg + + + mpeg + video/mpeg + + + mpega + audio/x-mpeg + + + mpg + video/mpeg + + + mpg4 + video/mp4 + + + mpga + audio/mpeg + + + mpkg + application/vnd.apple.installer+xml + + + mpm + application/vnd.blueice.multipass + + + mpn + application/vnd.mophun.application + + + mpp + application/vnd.ms-project + + + mpt + application/vnd.ms-project + + + mpv2 + video/mpeg2 + + + mpy + application/vnd.ibm.minipay + + + mqy + application/vnd.mobius.mqy + + + mrc + application/marc + + + mrcx + application/marcxml+xml + + + ms + text/troff + + + mscml + application/mediaservercontrol+xml + + + mseed + application/vnd.fdsn.mseed + + + mseq + application/vnd.mseq + + + msf + application/vnd.epson.msf + + + msh + model/mesh + + + msi + application/x-msdownload + + + msl + application/vnd.mobius.msl + + + msty + application/vnd.muvee.style + + + mts + model/vnd.mts + + + mus + application/vnd.musician + + + musicxml + application/vnd.recordare.musicxml+xml + + + mvb + application/x-msmediaview + + + mwf + application/vnd.mfer + + + mxf + application/mxf + + + mxl + application/vnd.recordare.musicxml + + + mxml + application/xv+xml + + + mxs + application/vnd.triscape.mxs + + + mxu + video/vnd.mpegurl + + + n-gage + application/vnd.nokia.n-gage.symbian.install + + + n3 + text/n3 + + + nb + application/mathematica + + + nbp + application/vnd.wolfram.player + + + nc + application/x-netcdf + + + ncx + application/x-dtbncx+xml + + + nfo + text/x-nfo + + + ngdat + application/vnd.nokia.n-gage.data + + + nitf + application/vnd.nitf + + + nlu + application/vnd.neurolanguage.nlu + + + nml + application/vnd.enliven + + + nnd + application/vnd.noblenet-directory + + + nns + application/vnd.noblenet-sealer + + + nnw + application/vnd.noblenet-web + + + npx + image/vnd.net-fpx + + + nsc + application/x-conference + + + nsf + application/vnd.lotus-notes + + + ntf + application/vnd.nitf + + + nzb + application/x-nzb + + + oa2 + application/vnd.fujitsu.oasys2 + + + oa3 + application/vnd.fujitsu.oasys3 + + + oas + application/vnd.fujitsu.oasys + + + obd + application/x-msbinder + + + obj + application/x-tgif + + + oda + application/oda + + + + odb + application/vnd.oasis.opendocument.database + + + + odc + application/vnd.oasis.opendocument.chart + + + + odf + application/vnd.oasis.opendocument.formula + + + odft + application/vnd.oasis.opendocument.formula-template + + + + odg + application/vnd.oasis.opendocument.graphics + + + + odi + application/vnd.oasis.opendocument.image + + + + odm + application/vnd.oasis.opendocument.text-master + + + + odp + application/vnd.oasis.opendocument.presentation + + + + ods + application/vnd.oasis.opendocument.spreadsheet + + + + odt + application/vnd.oasis.opendocument.text + + + oga + audio/ogg + + + ogg + audio/ogg + + + ogv + video/ogg + + + + ogx + application/ogg + + + omdoc + application/omdoc+xml + + + onepkg + application/onenote + + + onetmp + application/onenote + + + onetoc + application/onenote + + + onetoc2 + application/onenote + + + opf + application/oebps-package+xml + + + opml + text/x-opml + + + oprc + application/vnd.palm + + + org + application/vnd.lotus-organizer + + + osf + application/vnd.yamaha.openscoreformat + + + osfpvg + application/vnd.yamaha.openscoreformat.osfpvg+xml + + + otc + application/vnd.oasis.opendocument.chart-template + + + otf + font/otf + + + + otg + application/vnd.oasis.opendocument.graphics-template + + + + oth + application/vnd.oasis.opendocument.text-web + + + oti + application/vnd.oasis.opendocument.image-template + + + + otp + application/vnd.oasis.opendocument.presentation-template + + + + ots + application/vnd.oasis.opendocument.spreadsheet-template + + + + ott + application/vnd.oasis.opendocument.text-template + + + oxps + application/oxps + + + oxt + application/vnd.openofficeorg.extension + + + p + text/x-pascal + + + p10 + application/pkcs10 + + + p12 + application/x-pkcs12 + + + p7b + application/x-pkcs7-certificates + + + p7c + application/pkcs7-mime + + + p7m + application/pkcs7-mime + + + p7r + application/x-pkcs7-certreqresp + + + p7s + application/pkcs7-signature + + + p8 + application/pkcs8 + + + pas + text/x-pascal + + + paw + application/vnd.pawaafile + + + pbd + application/vnd.powerbuilder6 + + + pbm + image/x-portable-bitmap + + + pcap + application/vnd.tcpdump.pcap + + + pcf + application/x-font-pcf + + + pcl + application/vnd.hp-pcl + + + pclxl + application/vnd.hp-pclxl + + + pct + image/pict + + + pcurl + application/vnd.curl.pcurl + + + pcx + image/x-pcx + + + pdb + application/vnd.palm + + + pdf + application/pdf + + + pfa + application/x-font-type1 + + + pfb + application/x-font-type1 + + + pfm + application/x-font-type1 + + + pfr + application/font-tdpfr + + + pfx + application/x-pkcs12 + + + pgm + image/x-portable-graymap + + + pgn + application/x-chess-pgn + + + pgp + application/pgp-encrypted + + + pic + image/pict + + + pict + image/pict + + + pkg + application/octet-stream + + + pki + application/pkixcmp + + + pkipath + application/pkix-pkipath + + + plb + application/vnd.3gpp.pic-bw-large + + + plc + application/vnd.mobius.plc + + + plf + application/vnd.pocketlearn + + + pls + audio/x-scpls + + + pml + application/vnd.ctc-posml + + + png + image/png + + + pnm + image/x-portable-anymap + + + pnt + image/x-macpaint + + + portpkg + application/vnd.macports.portpkg + + + pot + application/vnd.ms-powerpoint + + + potm + application/vnd.ms-powerpoint.template.macroenabled.12 + + + potx + application/vnd.openxmlformats-officedocument.presentationml.template + + + ppam + application/vnd.ms-powerpoint.addin.macroenabled.12 + + + ppd + application/vnd.cups-ppd + + + ppm + image/x-portable-pixmap + + + pps + application/vnd.ms-powerpoint + + + ppsm + application/vnd.ms-powerpoint.slideshow.macroenabled.12 + + + ppsx + application/vnd.openxmlformats-officedocument.presentationml.slideshow + + + ppt + application/vnd.ms-powerpoint + + + pptm + application/vnd.ms-powerpoint.presentation.macroenabled.12 + + + pptx + application/vnd.openxmlformats-officedocument.presentationml.presentation + + + pqa + application/vnd.palm + + + prc + application/x-mobipocket-ebook + + + pre + application/vnd.lotus-freelance + + + prf + application/pics-rules + + + ps + application/postscript + + + psb + application/vnd.3gpp.pic-bw-small + + + psd + image/vnd.adobe.photoshop + + + psf + application/x-font-linux-psf + + + pskcxml + application/pskc+xml + + + ptid + application/vnd.pvi.ptid1 + + + pub + application/x-mspublisher + + + pvb + application/vnd.3gpp.pic-bw-var + + + pwn + application/vnd.3m.post-it-notes + + + pya + audio/vnd.ms-playready.media.pya + + + pyv + video/vnd.ms-playready.media.pyv + + + qam + application/vnd.epson.quickanime + + + qbo + application/vnd.intu.qbo + + + qfx + application/vnd.intu.qfx + + + qps + application/vnd.publishare-delta-tree + + + qt + video/quicktime + + + qti + image/x-quicktime + + + qtif + image/x-quicktime + + + qwd + application/vnd.quark.quarkxpress + + + qwt + application/vnd.quark.quarkxpress + + + qxb + application/vnd.quark.quarkxpress + + + qxd + application/vnd.quark.quarkxpress + + + qxl + application/vnd.quark.quarkxpress + + + qxt + application/vnd.quark.quarkxpress + + + ra + audio/x-pn-realaudio + + + ram + audio/x-pn-realaudio + + + rar + application/x-rar-compressed + + + ras + image/x-cmu-raster + + + rcprofile + application/vnd.ipunplugged.rcprofile + + + rdf + application/rdf+xml + + + rdz + application/vnd.data-vision.rdz + + + rep + application/vnd.businessobjects + + + res + application/x-dtbresource+xml + + + rgb + image/x-rgb + + + rif + application/reginfo+xml + + + rip + audio/vnd.rip + + + ris + application/x-research-info-systems + + + rl + application/resource-lists+xml + + + rlc + image/vnd.fujixerox.edmics-rlc + + + rld + application/resource-lists-diff+xml + + + rm + application/vnd.rn-realmedia + + + rmi + audio/midi + + + rmp + audio/x-pn-realaudio-plugin + + + rms + application/vnd.jcp.javame.midlet-rms + + + rmvb + application/vnd.rn-realmedia-vbr + + + rnc + application/relax-ng-compact-syntax + + + roa + application/rpki-roa + + + roff + text/troff + + + rp9 + application/vnd.cloanto.rp9 + + + rpss + application/vnd.nokia.radio-presets + + + rpst + application/vnd.nokia.radio-preset + + + rq + application/sparql-query + + + rs + application/rls-services+xml + + + rsd + application/rsd+xml + + + rss + application/rss+xml + + + rtf + application/rtf + + + rtx + text/richtext + + + s + text/x-asm + + + s3m + audio/s3m + + + saf + application/vnd.yamaha.smaf-audio + + + sbml + application/sbml+xml + + + sc + application/vnd.ibm.secure-container + + + scd + application/x-msschedule + + + scm + application/vnd.lotus-screencam + + + scq + application/scvp-cv-request + + + scs + application/scvp-cv-response + + + scurl + text/vnd.curl.scurl + + + sda + application/vnd.stardivision.draw + + + sdc + application/vnd.stardivision.calc + + + sdd + application/vnd.stardivision.impress + + + sdkd + application/vnd.solent.sdkm+xml + + + sdkm + application/vnd.solent.sdkm+xml + + + sdp + application/sdp + + + sdw + application/vnd.stardivision.writer + + + see + application/vnd.seemail + + + seed + application/vnd.fdsn.seed + + + sema + application/vnd.sema + + + semd + application/vnd.semd + + + semf + application/vnd.semf + + + ser + application/java-serialized-object + + + setpay + application/set-payment-initiation + + + setreg + application/set-registration-initiation + + + sfd-hdstx + application/vnd.hydrostatix.sof-data + + + sfs + application/vnd.spotfire.sfs + + + sfv + text/x-sfv + + + sgi + image/sgi + + + sgl + application/vnd.stardivision.writer-global + + + sgm + text/sgml + + + sgml + text/sgml + + + sh + application/x-sh + + + shar + application/x-shar + + + shf + application/shf+xml + + + + sid + image/x-mrsid-image + + + sig + application/pgp-signature + + + sil + audio/silk + + + silo + model/mesh + + + sis + application/vnd.symbian.install + + + sisx + application/vnd.symbian.install + + + sit + application/x-stuffit + + + sitx + application/x-stuffitx + + + skd + application/vnd.koan + + + skm + application/vnd.koan + + + skp + application/vnd.koan + + + skt + application/vnd.koan + + + sldm + application/vnd.ms-powerpoint.slide.macroenabled.12 + + + sldx + application/vnd.openxmlformats-officedocument.presentationml.slide + + + slt + application/vnd.epson.salt + + + sm + application/vnd.stepmania.stepchart + + + smf + application/vnd.stardivision.math + + + smi + application/smil+xml + + + smil + application/smil+xml + + + smv + video/x-smv + + + smzip + application/vnd.stepmania.package + + + snd + audio/basic + + + snf + application/x-font-snf + + + so + application/octet-stream + + + spc + application/x-pkcs7-certificates + + + spf + application/vnd.yamaha.smaf-phrase + + + spl + application/x-futuresplash + + + spot + text/vnd.in3d.spot + + + spp + application/scvp-vp-response + + + spq + application/scvp-vp-request + + + spx + audio/ogg + + + sql + application/x-sql + + + src + application/x-wais-source + + + srt + application/x-subrip + + + sru + application/sru+xml + + + srx + application/sparql-results+xml + + + ssdl + application/ssdl+xml + + + sse + application/vnd.kodak-descriptor + + + ssf + application/vnd.epson.ssf + + + ssml + application/ssml+xml + + + st + application/vnd.sailingtracker.track + + + stc + application/vnd.sun.xml.calc.template + + + std + application/vnd.sun.xml.draw.template + + + stf + application/vnd.wt.stf + + + sti + application/vnd.sun.xml.impress.template + + + stk + application/hyperstudio + + + stl + application/vnd.ms-pki.stl + + + str + application/vnd.pg.format + + + stw + application/vnd.sun.xml.writer.template + + + sub + text/vnd.dvb.subtitle + + + sus + application/vnd.sus-calendar + + + susp + application/vnd.sus-calendar + + + sv4cpio + application/x-sv4cpio + + + sv4crc + application/x-sv4crc + + + svc + application/vnd.dvb.service + + + svd + application/vnd.svd + + + svg + image/svg+xml + + + svgz + image/svg+xml + + + swa + application/x-director + + + swf + application/x-shockwave-flash + + + swi + application/vnd.aristanetworks.swi + + + sxc + application/vnd.sun.xml.calc + + + sxd + application/vnd.sun.xml.draw + + + sxg + application/vnd.sun.xml.writer.global + + + sxi + application/vnd.sun.xml.impress + + + sxm + application/vnd.sun.xml.math + + + sxw + application/vnd.sun.xml.writer + + + t + text/troff + + + t3 + application/x-t3vm-image + + + taglet + application/vnd.mynfc + + + tao + application/vnd.tao.intent-module-archive + + + tar + application/x-tar + + + tcap + application/vnd.3gpp2.tcap + + + tcl + application/x-tcl + + + teacher + application/vnd.smart.teacher + + + tei + application/tei+xml + + + teicorpus + application/tei+xml + + + tex + application/x-tex + + + texi + application/x-texinfo + + + texinfo + application/x-texinfo + + + text + text/plain + + + tfi + application/thraud+xml + + + tfm + application/x-tex-tfm + + + tga + image/x-tga + + + thmx + application/vnd.ms-officetheme + + + tif + image/tiff + + + tiff + image/tiff + + + tmo + application/vnd.tmobile-livetv + + + torrent + application/x-bittorrent + + + tpl + application/vnd.groove-tool-template + + + tpt + application/vnd.trid.tpt + + + tr + text/troff + + + tra + application/vnd.trueapp + + + trm + application/x-msterminal + + + tsd + application/timestamped-data + + + tsv + text/tab-separated-values + + + ttc + font/collection + + + ttf + font/ttf + + + ttl + text/turtle + + + twd + application/vnd.simtech-mindmapper + + + twds + application/vnd.simtech-mindmapper + + + txd + application/vnd.genomatix.tuxedo + + + txf + application/vnd.mobius.txf + + + txt + text/plain + + + u32 + application/x-authorware-bin + + + udeb + application/x-debian-package + + + ufd + application/vnd.ufdl + + + ufdl + application/vnd.ufdl + + + ulw + audio/basic + + + ulx + application/x-glulx + + + umj + application/vnd.umajin + + + unityweb + application/vnd.unity + + + uoml + application/vnd.uoml+xml + + + uri + text/uri-list + + + uris + text/uri-list + + + urls + text/uri-list + + + ustar + application/x-ustar + + + utz + application/vnd.uiq.theme + + + uu + text/x-uuencode + + + uva + audio/vnd.dece.audio + + + uvd + application/vnd.dece.data + + + uvf + application/vnd.dece.data + + + uvg + image/vnd.dece.graphic + + + uvh + video/vnd.dece.hd + + + uvi + image/vnd.dece.graphic + + + uvm + video/vnd.dece.mobile + + + uvp + video/vnd.dece.pd + + + uvs + video/vnd.dece.sd + + + uvt + application/vnd.dece.ttml+xml + + + uvu + video/vnd.uvvu.mp4 + + + uvv + video/vnd.dece.video + + + uvva + audio/vnd.dece.audio + + + uvvd + application/vnd.dece.data + + + uvvf + application/vnd.dece.data + + + uvvg + image/vnd.dece.graphic + + + uvvh + video/vnd.dece.hd + + + uvvi + image/vnd.dece.graphic + + + uvvm + video/vnd.dece.mobile + + + uvvp + video/vnd.dece.pd + + + uvvs + video/vnd.dece.sd + + + uvvt + application/vnd.dece.ttml+xml + + + uvvu + video/vnd.uvvu.mp4 + + + uvvv + video/vnd.dece.video + + + uvvx + application/vnd.dece.unspecified + + + uvvz + application/vnd.dece.zip + + + uvx + application/vnd.dece.unspecified + + + uvz + application/vnd.dece.zip + + + vcard + text/vcard + + + vcd + application/x-cdlink + + + vcf + text/x-vcard + + + vcg + application/vnd.groove-vcard + + + vcs + text/x-vcalendar + + + vcx + application/vnd.vcx + + + vis + application/vnd.visionary + + + viv + video/vnd.vivo + + + vob + video/x-ms-vob + + + vor + application/vnd.stardivision.writer + + + vox + application/x-authorware-bin + + + vrml + model/vrml + + + vsd + application/vnd.visio + + + vsf + application/vnd.vsf + + + vss + application/vnd.visio + + + vst + application/vnd.visio + + + vsw + application/vnd.visio + + + vtu + model/vnd.vtu + + + vxml + application/voicexml+xml + + + w3d + application/x-director + + + wad + application/x-doom + + + wav + audio/x-wav + + + wax + audio/x-ms-wax + + + + wbmp + image/vnd.wap.wbmp + + + wbs + application/vnd.criticaltools.wbs+xml + + + wbxml + application/vnd.wap.wbxml + + + wcm + application/vnd.ms-works + + + wdb + application/vnd.ms-works + + + wdp + image/vnd.ms-photo + + + weba + audio/webm + + + webm + video/webm + + + webp + image/webp + + + wg + application/vnd.pmi.widget + + + wgt + application/widget + + + wks + application/vnd.ms-works + + + wm + video/x-ms-wm + + + wma + audio/x-ms-wma + + + wmd + application/x-ms-wmd + + + wmf + application/x-msmetafile + + + + wml + text/vnd.wap.wml + + + + wmlc + application/vnd.wap.wmlc + + + + wmls + text/vnd.wap.wmlscript + + + + wmlsc + application/vnd.wap.wmlscriptc + + + wmv + video/x-ms-wmv + + + wmx + video/x-ms-wmx + + + wmz + application/x-msmetafile + + + woff + font/woff + + + woff2 + font/woff2 + + + wpd + application/vnd.wordperfect + + + wpl + application/vnd.ms-wpl + + + wps + application/vnd.ms-works + + + wqd + application/vnd.wqd + + + wri + application/x-mswrite + + + wrl + model/vrml + + + wsdl + application/wsdl+xml + + + wspolicy + application/wspolicy+xml + + + wtb + application/vnd.webturbo + + + wvx + video/x-ms-wvx + + + x32 + application/x-authorware-bin + + + x3d + model/x3d+xml + + + x3db + model/x3d+binary + + + x3dbz + model/x3d+binary + + + x3dv + model/x3d+vrml + + + x3dvz + model/x3d+vrml + + + x3dz + model/x3d+xml + + + xaml + application/xaml+xml + + + xap + application/x-silverlight-app + + + xar + application/vnd.xara + + + xbap + application/x-ms-xbap + + + xbd + application/vnd.fujixerox.docuworks.binder + + + xbm + image/x-xbitmap + + + xdf + application/xcap-diff+xml + + + xdm + application/vnd.syncml.dm+xml + + + xdp + application/vnd.adobe.xdp+xml + + + xdssc + application/dssc+xml + + + xdw + application/vnd.fujixerox.docuworks + + + xenc + application/xenc+xml + + + xer + application/patch-ops-error+xml + + + xfdf + application/vnd.adobe.xfdf + + + xfdl + application/vnd.xfdl + + + xht + application/xhtml+xml + + + xhtml + application/xhtml+xml + + + xhvml + application/xv+xml + + + xif + image/vnd.xiff + + + xla + application/vnd.ms-excel + + + xlam + application/vnd.ms-excel.addin.macroenabled.12 + + + xlc + application/vnd.ms-excel + + + xlf + application/x-xliff+xml + + + xlm + application/vnd.ms-excel + + + xls + application/vnd.ms-excel + + + xlsb + application/vnd.ms-excel.sheet.binary.macroenabled.12 + + + xlsm + application/vnd.ms-excel.sheet.macroenabled.12 + + + xlsx + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + + + xlt + application/vnd.ms-excel + + + xltm + application/vnd.ms-excel.template.macroenabled.12 + + + xltx + application/vnd.openxmlformats-officedocument.spreadsheetml.template + + + xlw + application/vnd.ms-excel + + + xm + audio/xm + + + xml + application/xml + + + xo + application/vnd.olpc-sugar + + + xop + application/xop+xml + + + xpi + application/x-xpinstall + + + xpl + application/xproc+xml + + + xpm + image/x-xpixmap + + + xpr + application/vnd.is-xpr + + + xps + application/vnd.ms-xpsdocument + + + xpw + application/vnd.intercon.formnet + + + xpx + application/vnd.intercon.formnet + + + xsl + application/xml + + + xslt + application/xslt+xml + + + xsm + application/vnd.syncml+xml + + + xspf + application/xspf+xml + + + xul + application/vnd.mozilla.xul+xml + + + xvm + application/xv+xml + + + xvml + application/xv+xml + + + xwd + image/x-xwindowdump + + + xyz + chemical/x-xyz + + + xz + application/x-xz + + + yang + application/yang + + + yin + application/yin+xml + + + z + application/x-compress + + + Z + application/x-compress + + + z1 + application/x-zmachine + + + z2 + application/x-zmachine + + + z3 + application/x-zmachine + + + z4 + application/x-zmachine + + + z5 + application/x-zmachine + + + z6 + application/x-zmachine + + + z7 + application/x-zmachine + + + z8 + application/x-zmachine + + + zaz + application/vnd.zzazz.deck+xml + + + zip + application/zip + + + zir + application/vnd.zul + + + zirz + application/vnd.zul + + + zmm + application/vnd.handheld-entertainment+xml + + + + + + + + + + + + + + + + + + index.html + index.htm + index.jsp + + + \ No newline at end of file diff --git a/crudj2e/pom.xml b/crudj2e/pom.xml new file mode 100644 index 0000000..12be2c2 --- /dev/null +++ b/crudj2e/pom.xml @@ -0,0 +1,52 @@ + + 4.0.0 + com.slackroute + CRUDJ2E + war + 0.0.1-SNAPSHOT + CRUDJ2E Maven Webapp + http://maven.apache.org + + 1.6 + 1.6 + + + + junit + junit + 3.8.1 + test + + + + mysql + mysql-connector-java + 8.0.13 + + + + + javax.servlet + jstl + 1.2 + + + jstl + jstl + 1.2 + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + + CRUDJ2E + + + + diff --git a/crudj2e/src/main/java/com/stackroute/dao/MovieDao.java b/crudj2e/src/main/java/com/stackroute/dao/MovieDao.java new file mode 100644 index 0000000..653e80d --- /dev/null +++ b/crudj2e/src/main/java/com/stackroute/dao/MovieDao.java @@ -0,0 +1,22 @@ +package com.stackroute.dao; + +import java.util.List; + +import com.stackroute.domain.Movie; + +/** + * The MovieDao program is an interface that + * has method signatures for CRUD operations. + * + * @author Shri Ramya + */ + +public interface MovieDao { + + public void createMovieTable(); + public List getAllMovies() ; + public Movie saveMovie(Movie movieObj); + public Movie updateMovie(Movie movieObj); + public String deleteMovie(String movieId); + +} diff --git a/crudj2e/src/main/java/com/stackroute/dao/MovieDaoImpl.java b/crudj2e/src/main/java/com/stackroute/dao/MovieDaoImpl.java new file mode 100644 index 0000000..dcb8975 --- /dev/null +++ b/crudj2e/src/main/java/com/stackroute/dao/MovieDaoImpl.java @@ -0,0 +1,139 @@ +package com.stackroute.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.List; +import com.stackroute.domain.Movie; + +/** + * The MovieDaoImpl program implements all the four + * methods defined in MovieDao interface. Prepared + * statements are used and queries are executed. + * + * @author Shri Ramya + */ +public class MovieDaoImpl implements MovieDao { + + private Connection con; + private Context ctx; + //Establishing connection to mysql database + public MovieDaoImpl() { + super(); + try { + ctx = new InitialContext(); + DataSource dataSource=(DataSource) ctx.lookup("java:comp/env/jdbc/ds1"); + con=dataSource.getConnection(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public List getAllMovies() { + List movies = new ArrayList(); + String sql="select * from movie"; + PreparedStatement pstmt = null; + try { + pstmt = con.prepareStatement(sql); + ResultSet rs=pstmt.executeQuery(); + while(rs.next()) { + Movie movieObj = new Movie(); + movieObj.setMovieid(rs.getString(1)); + movieObj.setMovieName(rs.getString(2)); + movieObj.setMovieGenre(rs.getString(3)); + movies.add(movieObj); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return movies; + } + + public Movie saveMovie(Movie movieObj) { + Movie movieObjOne = new Movie(); + int rs=0; + String sql="insert into movie values(?,?,?)"; + PreparedStatement pstmt = null; + try { + pstmt = con.prepareStatement(sql); + pstmt.setString(1, movieObj.getMovieid()); + pstmt.setString(2, movieObj.getMovieName()); + pstmt.setString(3, movieObj.getMovieGenre()); + rs=pstmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + if(rs!=0) { + return movieObj; + } + return movieObjOne; + } + + public Movie updateMovie(Movie movieObj) { + Movie movieObjOne = new Movie(); + int rs=0; + String sql="update movie set movieid=?, movieName=?, movieGenre=? where movieid=?"; + PreparedStatement pstmt = null; + try { + pstmt = con.prepareStatement(sql); + pstmt.setString(1, movieObj.getMovieid()); + pstmt.setString(2, movieObj.getMovieName()); + pstmt.setString(3, movieObj.getMovieGenre()); + pstmt.setString(4, movieObj.getMovieid()); + rs=pstmt.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + if(rs!=0) { + return movieObj; + } + return movieObjOne; + } + + public String deleteMovie(String movieId) { + int rs=0; + String sql="delete from movie where movieid=?"; + PreparedStatement pstmt = null; + try { + pstmt = con.prepareStatement(sql); + pstmt.setString(1, movieId); + rs=pstmt.executeUpdate(); + } catch (SQLException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if(rs!=0) { + return "success"; + } + return null; + } + + public void createMovieTable() { + PreparedStatement pstmt = null; + String sql1 = "SELECT count(*)\n" + + "FROM information_schema.TABLES\n" + + "WHERE (TABLE_SCHEMA = 'mysql') AND (TABLE_NAME = 'movie')"; + + String sql2 = "create table movie(movieid varchar(25) not null, " + + "movieName varchar(100) not null, movieGenre varchar(100) not null,primary key (movieid))"; + + try { + pstmt = con.prepareStatement(sql1); + ResultSet rs = pstmt.executeQuery(); + if(rs.next()) { + if(rs.getInt(1)==0) { + pstmt = con.prepareStatement(sql2); + pstmt.executeUpdate(); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + +} diff --git a/crudj2e/src/main/java/com/stackroute/domain/Movie.java b/crudj2e/src/main/java/com/stackroute/domain/Movie.java new file mode 100644 index 0000000..3fa8c9c --- /dev/null +++ b/crudj2e/src/main/java/com/stackroute/domain/Movie.java @@ -0,0 +1,46 @@ +package com.stackroute.domain; + +/** + * The Movie program is a domain class. + * + * @author Shri Ramya + */ +public class Movie { + + private String movieid; + private String movieName; + private String movieGenre; + + public Movie() { + super(); + } + public Movie(String movieid, String movieName, String movieGenre) { + super(); + this.movieid = movieid; + this.movieName = movieName; + this.movieGenre = movieGenre; + } + public String getMovieid() { + return movieid; + } + public void setMovieid(String movieid) { + this.movieid = movieid; + } + public String getMovieName() { + return movieName; + } + public void setMovieName(String movieName) { + this.movieName = movieName; + } + public String getMovieGenre() { + return movieGenre; + } + public void setMovieGenre(String movieGenre) { + this.movieGenre = movieGenre; + } + @Override + public String toString() { + return "Movie [movieid=" + movieid + ", movieName=" + movieName + ", movieGenre=" + movieGenre + "]"; + } + +} diff --git a/crudj2e/src/main/java/com/stackroute/web/UDController.java b/crudj2e/src/main/java/com/stackroute/web/UDController.java new file mode 100644 index 0000000..92f8990 --- /dev/null +++ b/crudj2e/src/main/java/com/stackroute/web/UDController.java @@ -0,0 +1,83 @@ +package com.stackroute.web; + +import java.io.IOException; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.stackroute.dao.MovieDaoImpl; +import com.stackroute.domain.Movie; + +/** + * Servlet implementation class UDController + * + * @author Shri Ramya + */ +public class UDController extends HttpServlet { + private static final long serialVersionUID = 1L; + + /** + * @see HttpServlet#HttpServlet() + */ + public UDController() { + super(); + // TODO Auto-generated constructor stub + } + + /** + * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) + */ + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // TODO Auto-generated method stub + //response.getWriter().append("Served at: ").append(request.getContextPath()); + String idSelected = (String) request.getParameter("mId"); + String nameSelected = (String) request.getParameter("mName"); + String genreSelected = (String) request.getParameter("mGenre"); + + if(request.getParameter("action").equals("update")) { + request.setAttribute("id", idSelected); + request.setAttribute("name", nameSelected); + request.setAttribute("genre", genreSelected); + RequestDispatcher rd = request.getRequestDispatcher("update.jsp"); + rd.forward(request, response); + }else if(request.getParameter("action").equals("delete")) { + MovieDaoImpl movieDaoObj = new MovieDaoImpl(); + movieDaoObj.deleteMovie(idSelected); + movieController movieControllerObj = new movieController(); + movieControllerObj.doGet(request, response); + // RequestDispatcher rd = request.getRequestDispatcher("delete.jsp"); + // rd.forward(request, response); + } + + } + + /** + * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) + */ + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + MovieDaoImpl movieDaoImplObj = new MovieDaoImpl(); + String idSelected = (String) request.getParameter("mId"); + String nameSelected = (String) request.getParameter("mName"); + String genreSelected = (String) request.getParameter("mGenre"); + + Movie movieObj = + new Movie(idSelected,nameSelected,genreSelected); + Movie resultMovie = movieDaoImplObj.updateMovie(movieObj); + if(resultMovie!=null) { + request.setAttribute("result", "Update Successfull!"); + request.setAttribute("id", idSelected); + request.setAttribute("name", nameSelected); + request.setAttribute("genre", genreSelected); + }else { + request.setAttribute("result", "Update Unsuccessfull!"); + } + + RequestDispatcher rd = request.getRequestDispatcher("update.jsp"); + rd.forward(request, response); + //doGet(request, response); + } + +} diff --git a/crudj2e/src/main/java/com/stackroute/web/movieController.java b/crudj2e/src/main/java/com/stackroute/web/movieController.java new file mode 100644 index 0000000..af937ce --- /dev/null +++ b/crudj2e/src/main/java/com/stackroute/web/movieController.java @@ -0,0 +1,71 @@ +package com.stackroute.web; + +import java.io.IOException; +import java.util.List; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import com.stackroute.dao.MovieDaoImpl; +import com.stackroute.domain.Movie; + +/** + * Servlet implementation class movieController + * + * @author Shri Ramya + */ +public class movieController extends HttpServlet { + private static final long serialVersionUID = 1L; + + /** + * @see HttpServlet#HttpServlet() + */ + public movieController() { + super(); + // TODO Auto-generated constructor stub + } + + + + @Override + public void init() throws ServletException { + // TODO Auto-generated method stub + super.init(); + MovieDaoImpl movieDaoObj = new MovieDaoImpl(); + movieDaoObj.createMovieTable(); + } + + + + /** + * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) + */ + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + System.out.println("Incoming request 111111111111"); + MovieDaoImpl movieDaoObj = new MovieDaoImpl(); + List movies = movieDaoObj.getAllMovies(); + + request.setAttribute("MovieList", movies); + RequestDispatcher rd = request.getRequestDispatcher("showMovies.jsp"); + rd.forward(request, response); + //response.getWriter().append("Served at: ").append(request.getContextPath()); + } + + /** + * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) + */ + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // TODO Auto-generated method stub + //doGet(request, response); + Movie movieObj= + new Movie(request.getParameter("mId"),request.getParameter("mName"),request.getParameter("genre")); + MovieDaoImpl movieDaoObj = new MovieDaoImpl(); + Movie resultMovie = movieDaoObj.saveMovie(movieObj); + request.setAttribute("MovieResult", resultMovie); + RequestDispatcher rd = request.getRequestDispatcher("addMovie.jsp"); + rd.forward(request, response); + } + +} diff --git a/crudj2e/src/main/webapp/META-INF/context.xml b/crudj2e/src/main/webapp/META-INF/context.xml new file mode 100644 index 0000000..128aa5d --- /dev/null +++ b/crudj2e/src/main/webapp/META-INF/context.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + diff --git a/crudj2e/src/main/webapp/WEB-INF/web.xml b/crudj2e/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..a9ceb7b --- /dev/null +++ b/crudj2e/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,27 @@ + + + + Archetype Created Web Application + + movieController + movieController + + com.stackroute.web.movieController + + + UDController + UDController + + com.stackroute.web.UDController + + + movieController + /movie + + + UDController + /UDController + + diff --git a/crudj2e/src/main/webapp/addMovie.jsp b/crudj2e/src/main/webapp/addMovie.jsp new file mode 100644 index 0000000..cc6dfa7 --- /dev/null +++ b/crudj2e/src/main/webapp/addMovie.jsp @@ -0,0 +1,47 @@ +<%@page import="com.stackroute.domain.* "%> +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +Movie-Add + + + + Home +
+ +
+ Add Movie + + +
+ MovieId: MovieName: Genre: + + <%Movie mvi = + (Movie)request.getAttribute("MovieResult"); + if(mvi!=null){%> +

You added:

+ + + + + + + + <%-- Arranging data in tabular form + --%> + + + + + + +
Movie IdMovie NameMovie Genre
<%=mvi.getMovieid()%><%=mvi.getMovieName()%><%=mvi.getMovieGenre()%>
+ <%}%> + + + \ No newline at end of file diff --git a/crudj2e/src/main/webapp/index.jsp b/crudj2e/src/main/webapp/index.jsp new file mode 100644 index 0000000..2f82544 --- /dev/null +++ b/crudj2e/src/main/webapp/index.jsp @@ -0,0 +1,16 @@ + + +Movie-Home + + +

Hello Movie Enthusists!!

+ + Home + Add Movie + + +
+ + + + diff --git a/crudj2e/src/main/webapp/showMovies.jsp b/crudj2e/src/main/webapp/showMovies.jsp new file mode 100644 index 0000000..570008b --- /dev/null +++ b/crudj2e/src/main/webapp/showMovies.jsp @@ -0,0 +1,53 @@ +<%@page import="com.stackroute.domain.Movie , java.util.List"%> + +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +MovieList + + + Home +
+ +
+ Add Movie +

Here is/are the List of all the movies you have saved:

+ + + + + + + + + <%List mvi = + (List)request.getAttribute("MovieList"); + for(Movie m:mvi){ + + + %> + <%-- Arranging data in tabular form + --%> + + + + + + + + + + + + <%}%> +
Movie IdMovie NameMovie GenreActions
<%=m.getMovieid()%><%=m.getMovieName()%><%=m.getMovieGenre()%> + + +
+ + + + \ No newline at end of file diff --git a/crudj2e/src/main/webapp/update.jsp b/crudj2e/src/main/webapp/update.jsp new file mode 100644 index 0000000..d14cc47 --- /dev/null +++ b/crudj2e/src/main/webapp/update.jsp @@ -0,0 +1,52 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" + pageEncoding="UTF-8"%> + + + + +Movie-Update + + + Home +
+ +
+ Add Movie + <% + String id = (String) request.getAttribute("id"); + String name = (String) request.getAttribute("name"); + String genre = (String) request.getAttribute("genre"); + %> + + + + + + + + + <%-- Arranging data in tabular form + --%> + + + + + + + + + + +
Movie IdMovie NameMovie Genre
<%=id%>
+ + + <% + String result = (String) request.getAttribute("result"); + if (result != null) { + %> +

<%=result%>

+ <% + } + %> + + \ No newline at end of file From 897e54caafbca31ee736c5bcf0e7c20609031d7e Mon Sep 17 00:00:00 2001 From: "avinash.pai" Date: Mon, 11 Feb 2019 15:42:18 +0530 Subject: [PATCH 02/38] added node crud application --- muzixappNode/.dockerignore | 2 + muzixappNode/Dockerfile | 17 + .../app/controllers/muzix.controller.js | 99 ++++ muzixappNode/app/model/muzix.model.js | 12 + muzixappNode/app/routes/muzix.routes.js | 20 + muzixappNode/config/database.config.js | 3 + muzixappNode/package-lock.json | 531 ++++++++++++++++++ muzixappNode/package.json | 17 + muzixappNode/server.js | 39 ++ 9 files changed, 740 insertions(+) create mode 100644 muzixappNode/.dockerignore create mode 100644 muzixappNode/Dockerfile create mode 100644 muzixappNode/app/controllers/muzix.controller.js create mode 100644 muzixappNode/app/model/muzix.model.js create mode 100644 muzixappNode/app/routes/muzix.routes.js create mode 100644 muzixappNode/config/database.config.js create mode 100644 muzixappNode/package-lock.json create mode 100644 muzixappNode/package.json create mode 100644 muzixappNode/server.js diff --git a/muzixappNode/.dockerignore b/muzixappNode/.dockerignore new file mode 100644 index 0000000..93f1361 --- /dev/null +++ b/muzixappNode/.dockerignore @@ -0,0 +1,2 @@ +node_modules +npm-debug.log diff --git a/muzixappNode/Dockerfile b/muzixappNode/Dockerfile new file mode 100644 index 0000000..5c901f8 --- /dev/null +++ b/muzixappNode/Dockerfile @@ -0,0 +1,17 @@ +FROM node:10-alpine + +# Create app directory +WORKDIR /home/user/Desktop/Avinash/SpringBoot/muzixappNode + + +COPY package*.json ./ + +RUN npm install +# If you are building your code for production +# RUN npm install --only=production + +# Bundle app source +COPY . . + +#EXPOSE 8082 +CMD [ "npm", "start" ] diff --git a/muzixappNode/app/controllers/muzix.controller.js b/muzixappNode/app/controllers/muzix.controller.js new file mode 100644 index 0000000..f4c82f7 --- /dev/null +++ b/muzixappNode/app/controllers/muzix.controller.js @@ -0,0 +1,99 @@ +const Muzix = require('../model/muzix.model.js'); + +// Create and Save a new Track +exports.create = (req, res) => { + // Validate request + if(!req.body.trackId) { + return res.status(400).send({ + message: "Track content can not be empty" + }); + } + + // Create a Track + const track = new Muzix({ + trackId: req.body.trackId, + trackName: req.body.trackName, + trackComments: req.body.trackComments + }); + + // Save Track in the database + track.save() + .then(data => { + res.send(data); + }).catch(err => { + res.status(500).send({ + message: err.message || "Some error occurred while creating the Note." + }); + }); +}; + +// Retrieve and return all tracks from the database. +exports.findAll = (req, res) => { + Muzix.find() + .then( muzix => { + res.send(muzix); + }).catch(err => { + res.status(500).send({ + message: err.message || "Some error occurred while retrieving notes." + }); + }); +}; + +//checking for connection +exports.enter = (rep, res) => { + res.send("Hello"); +}; +// Update a note identified by the noteId in the request +exports.update = (req, res) => { + // Validate Request + if(!req.body.content) { + return res.status(400).send({ + message: "Note content can not be empty" + }); + } + + // Find note and update it with the request body + Note.findByIdAndUpdate(req.params.id, { + trackComments: req.body.comments + }, {new: true}) + .then(track => { + if(!track) { + return res.status(404).send({ + message: "Note not found with id " + req.params.id + }); + } + res.send(track); + }).catch(err => { + if(err.kind === 'ObjectId') { + return res.status(404).send({ + message: "Note not found with id " + req.params.id + }); + } + return res.status(500).send({ + message: "Error updating note with id " + req.params.id + }); + }); +}; + + +// Delete a note with the specified noteId in the request +exports.delete = (req, res) => { + Note.findByIdAndRemove(req.params.id) + .then(track => { + if(!track) { + return res.status(404).send({ + message: "Note not found with id " + req.params.id + }); + } + res.send({message: "Note deleted successfully!"}); + }).catch(err => { + if(err.kind === 'ObjectId' || err.name === 'NotFound') { + return res.status(404).send({ + message: "Note not found with id " + req.params.id + }); + } + return res.status(500).send({ + message: "Could not delete note with id " + req.params.id + }); + }); +}; diff --git a/muzixappNode/app/model/muzix.model.js b/muzixappNode/app/model/muzix.model.js new file mode 100644 index 0000000..051fc3d --- /dev/null +++ b/muzixappNode/app/model/muzix.model.js @@ -0,0 +1,12 @@ +const mongoose = require('mongoose'); + +const MuzixSchema = mongoose.Schema({ + trackId: String, + trackName: String, + trackComments: String +}, { + timestamps: true +}); + +module.exports = mongoose.model('Muzix', MuzixSchema, 'mymuzix'); + diff --git a/muzixappNode/app/routes/muzix.routes.js b/muzixappNode/app/routes/muzix.routes.js new file mode 100644 index 0000000..74b4ff8 --- /dev/null +++ b/muzixappNode/app/routes/muzix.routes.js @@ -0,0 +1,20 @@ +module.exports = (app) => { + const muzix = require('../controllers/muzix.controller.js'); + + //checking for connection + app.get('/hi', muzix.enter); + // Create a new Note + app.post('/track', muzix.create); + + // Retrieve all Notes + app.get('/tracks', muzix.findAll); + + // Retrieve a single Note with noteId + // app.get('/track/:id', notes.findOne); + + // Update a Note with noteId + app.put('/track/:comments/:id', muzix.update); + + // Delete a Note with noteId + app.delete('/track/:id', muzix.delete); +} diff --git a/muzixappNode/config/database.config.js b/muzixappNode/config/database.config.js new file mode 100644 index 0000000..98b1d14 --- /dev/null +++ b/muzixappNode/config/database.config.js @@ -0,0 +1,3 @@ +module.exports = { + url: 'mongodb://localhost:27017/muzixnodedb' +} diff --git a/muzixappNode/package-lock.json b/muzixappNode/package-lock.json new file mode 100644 index 0000000..832dcba --- /dev/null +++ b/muzixappNode/package-lock.json @@ -0,0 +1,531 @@ +{ + "name": "muzixappNode", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "^4.17.10" + } + }, + "bluebird": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bson": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.0.tgz", + "integrity": "sha512-9Aeai9TacfNtWXOYarkFJRW2CWo+dRon+fuLZYJmvLV3+MiUp0bEI6IAZfXEIg7/Pl/7IWlLaDnhzTsD81etQA==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=" + }, + "kareem": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz", + "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg==" + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "requires": { + "mime-db": "~1.37.0" + } + }, + "mongodb": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.1.10.tgz", + "integrity": "sha512-Uml42GeFxhTGQVml1XQ4cD0o/rp7J2ROy0fdYUcVitoE7vFqEhKH4TYVqRDpQr/bXtCJVxJdNQC1ntRxNREkPQ==", + "requires": { + "mongodb-core": "3.1.9", + "safe-buffer": "^5.1.2" + } + }, + "mongodb-core": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.1.9.tgz", + "integrity": "sha512-MJpciDABXMchrZphh3vMcqu8hkNf/Mi+Gk6btOimVg1XMxLXh87j6FAvRm+KmwD1A9fpu3qRQYcbQe4egj23og==", + "requires": { + "bson": "^1.1.0", + "require_optional": "^1.0.1", + "safe-buffer": "^5.1.2", + "saslprep": "^1.0.0" + } + }, + "mongoose": { + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.4.9.tgz", + "integrity": "sha512-4dSQpDUe/9b7A7dRrsyJfWmoGEpeMaZ/WZ/KIJFqTHbJm3NUWaWF++hhirAgjtoHNq2ZILIII0LHEhgzP2NuRw==", + "requires": { + "async": "2.6.1", + "bson": "~1.1.0", + "kareem": "2.3.0", + "mongodb": "3.1.10", + "mongodb-core": "3.1.9", + "mongoose-legacy-pluralize": "1.0.2", + "mpath": "0.5.1", + "mquery": "3.2.0", + "ms": "2.0.0", + "regexp-clone": "0.0.1", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + } + }, + "mongoose-legacy-pluralize": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", + "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + }, + "mpath": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.5.1.tgz", + "integrity": "sha512-H8OVQ+QEz82sch4wbODFOz+3YQ61FYz/z3eJ5pIdbMEaUzDqA268Wd+Vt4Paw9TJfvDgVKaayC0gBzMIw2jhsg==" + }, + "mquery": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.0.tgz", + "integrity": "sha512-qPJcdK/yqcbQiKoemAt62Y0BAc0fTEKo1IThodBD+O5meQRJT/2HSe5QpBNwaa4CjskoGrYWsEyjkqgiE0qjhg==", + "requires": { + "bluebird": "3.5.1", + "debug": "3.1.0", + "regexp-clone": "0.0.1", + "safe-buffer": "5.1.2", + "sliced": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "regexp-clone": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", + "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" + }, + "require_optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", + "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", + "requires": { + "resolve-from": "^2.0.0", + "semver": "^5.1.0" + } + }, + "resolve-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", + "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "saslprep": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.2.tgz", + "integrity": "sha512-4cDsYuAjXssUSjxHKRe4DTZC0agDwsCqcMqtJAQPzC74nJ7LfAJflAtC1Zed5hMzEQKj82d3tuzqdGNRsLJ4Gw==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + }, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/muzixappNode/package.json b/muzixappNode/package.json new file mode 100644 index 0000000..01f548b --- /dev/null +++ b/muzixappNode/package.json @@ -0,0 +1,17 @@ +{ + "name": "muzixappNode", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.18.3", + "express": "^4.16.4", + "mongoose": "^5.4.9" + } +} diff --git a/muzixappNode/server.js b/muzixappNode/server.js new file mode 100644 index 0000000..91d9415 --- /dev/null +++ b/muzixappNode/server.js @@ -0,0 +1,39 @@ +const express = require('express'); +const bodyParser = require('body-parser'); + +// create express app +const app = express(); + +// parse requests of content-type - application/x-www-form-urlencoded +app.use(bodyParser.urlencoded({ extended: true })) + +// parse requests of content-type - application/json +app.use(bodyParser.json()) + +// define a simple route +app.get('/', (req, res) => { + res.json({"message": "Welcome to EasyNotes application. Take notes quickly. Organize and keep track of all your notes."}); +}); + + +require('./app/routes/muzix.routes.js')(app); +// listen for requests +app.listen(8082, () => { + console.log("Server is listening on port 8082"); +}); + +// Configuring the database +const dbConfig = require('./config/database.config.js'); +const mongoose = require('mongoose'); + +mongoose.Promise = global.Promise; + +// Connecting to the database +mongoose.connect(dbConfig.url, { + useNewUrlParser: true +}).then(() => { + console.log("Successfully connected to the database"); +}).catch(err => { + console.log('Could not connect to the database. Exiting now...', err); + process.exit(); +}); From 0df60290bfc093092e962a9bec3b7127e58f311e Mon Sep 17 00:00:00 2001 From: Shri Date: Mon, 11 Feb 2019 16:04:44 +0530 Subject: [PATCH 03/38] J2E test env --- crudj2e/{ => CRUDJ2E}/Dockerfile | 0 crudj2e/{ => CRUDJ2E}/README.md | 0 crudj2e/{ => CRUDJ2E}/pom.xml | 0 .../java/com/stackroute/dao/MovieDao.java | 0 .../java/com/stackroute/dao/MovieDaoImpl.java | 0 .../java/com/stackroute/domain/Movie.java | 0 .../java/com/stackroute/web/UDController.java | 0 .../com/stackroute/web/movieController.java | 0 .../src/main/webapp/META-INF/context.xml | 0 .../src/main/webapp/WEB-INF/web.xml | 0 .../src/main/webapp/addMovie.jsp | 0 .../{ => CRUDJ2E}/src/main/webapp/index.jsp | 0 .../src/main/webapp/showMovies.jsp | 0 .../{ => CRUDJ2E}/src/main/webapp/update.jsp | 0 crudj2e/docker-compose.yml | 23 +++++++++++++++++++ 15 files changed, 23 insertions(+) rename crudj2e/{ => CRUDJ2E}/Dockerfile (100%) rename crudj2e/{ => CRUDJ2E}/README.md (100%) rename crudj2e/{ => CRUDJ2E}/pom.xml (100%) rename crudj2e/{ => CRUDJ2E}/src/main/java/com/stackroute/dao/MovieDao.java (100%) rename crudj2e/{ => CRUDJ2E}/src/main/java/com/stackroute/dao/MovieDaoImpl.java (100%) rename crudj2e/{ => CRUDJ2E}/src/main/java/com/stackroute/domain/Movie.java (100%) rename crudj2e/{ => CRUDJ2E}/src/main/java/com/stackroute/web/UDController.java (100%) rename crudj2e/{ => CRUDJ2E}/src/main/java/com/stackroute/web/movieController.java (100%) rename crudj2e/{ => CRUDJ2E}/src/main/webapp/META-INF/context.xml (100%) rename crudj2e/{ => CRUDJ2E}/src/main/webapp/WEB-INF/web.xml (100%) rename crudj2e/{ => CRUDJ2E}/src/main/webapp/addMovie.jsp (100%) rename crudj2e/{ => CRUDJ2E}/src/main/webapp/index.jsp (100%) rename crudj2e/{ => CRUDJ2E}/src/main/webapp/showMovies.jsp (100%) rename crudj2e/{ => CRUDJ2E}/src/main/webapp/update.jsp (100%) create mode 100644 crudj2e/docker-compose.yml diff --git a/crudj2e/Dockerfile b/crudj2e/CRUDJ2E/Dockerfile similarity index 100% rename from crudj2e/Dockerfile rename to crudj2e/CRUDJ2E/Dockerfile diff --git a/crudj2e/README.md b/crudj2e/CRUDJ2E/README.md similarity index 100% rename from crudj2e/README.md rename to crudj2e/CRUDJ2E/README.md diff --git a/crudj2e/pom.xml b/crudj2e/CRUDJ2E/pom.xml similarity index 100% rename from crudj2e/pom.xml rename to crudj2e/CRUDJ2E/pom.xml diff --git a/crudj2e/src/main/java/com/stackroute/dao/MovieDao.java b/crudj2e/CRUDJ2E/src/main/java/com/stackroute/dao/MovieDao.java similarity index 100% rename from crudj2e/src/main/java/com/stackroute/dao/MovieDao.java rename to crudj2e/CRUDJ2E/src/main/java/com/stackroute/dao/MovieDao.java diff --git a/crudj2e/src/main/java/com/stackroute/dao/MovieDaoImpl.java b/crudj2e/CRUDJ2E/src/main/java/com/stackroute/dao/MovieDaoImpl.java similarity index 100% rename from crudj2e/src/main/java/com/stackroute/dao/MovieDaoImpl.java rename to crudj2e/CRUDJ2E/src/main/java/com/stackroute/dao/MovieDaoImpl.java diff --git a/crudj2e/src/main/java/com/stackroute/domain/Movie.java b/crudj2e/CRUDJ2E/src/main/java/com/stackroute/domain/Movie.java similarity index 100% rename from crudj2e/src/main/java/com/stackroute/domain/Movie.java rename to crudj2e/CRUDJ2E/src/main/java/com/stackroute/domain/Movie.java diff --git a/crudj2e/src/main/java/com/stackroute/web/UDController.java b/crudj2e/CRUDJ2E/src/main/java/com/stackroute/web/UDController.java similarity index 100% rename from crudj2e/src/main/java/com/stackroute/web/UDController.java rename to crudj2e/CRUDJ2E/src/main/java/com/stackroute/web/UDController.java diff --git a/crudj2e/src/main/java/com/stackroute/web/movieController.java b/crudj2e/CRUDJ2E/src/main/java/com/stackroute/web/movieController.java similarity index 100% rename from crudj2e/src/main/java/com/stackroute/web/movieController.java rename to crudj2e/CRUDJ2E/src/main/java/com/stackroute/web/movieController.java diff --git a/crudj2e/src/main/webapp/META-INF/context.xml b/crudj2e/CRUDJ2E/src/main/webapp/META-INF/context.xml similarity index 100% rename from crudj2e/src/main/webapp/META-INF/context.xml rename to crudj2e/CRUDJ2E/src/main/webapp/META-INF/context.xml diff --git a/crudj2e/src/main/webapp/WEB-INF/web.xml b/crudj2e/CRUDJ2E/src/main/webapp/WEB-INF/web.xml similarity index 100% rename from crudj2e/src/main/webapp/WEB-INF/web.xml rename to crudj2e/CRUDJ2E/src/main/webapp/WEB-INF/web.xml diff --git a/crudj2e/src/main/webapp/addMovie.jsp b/crudj2e/CRUDJ2E/src/main/webapp/addMovie.jsp similarity index 100% rename from crudj2e/src/main/webapp/addMovie.jsp rename to crudj2e/CRUDJ2E/src/main/webapp/addMovie.jsp diff --git a/crudj2e/src/main/webapp/index.jsp b/crudj2e/CRUDJ2E/src/main/webapp/index.jsp similarity index 100% rename from crudj2e/src/main/webapp/index.jsp rename to crudj2e/CRUDJ2E/src/main/webapp/index.jsp diff --git a/crudj2e/src/main/webapp/showMovies.jsp b/crudj2e/CRUDJ2E/src/main/webapp/showMovies.jsp similarity index 100% rename from crudj2e/src/main/webapp/showMovies.jsp rename to crudj2e/CRUDJ2E/src/main/webapp/showMovies.jsp diff --git a/crudj2e/src/main/webapp/update.jsp b/crudj2e/CRUDJ2E/src/main/webapp/update.jsp similarity index 100% rename from crudj2e/src/main/webapp/update.jsp rename to crudj2e/CRUDJ2E/src/main/webapp/update.jsp diff --git a/crudj2e/docker-compose.yml b/crudj2e/docker-compose.yml new file mode 100644 index 0000000..4343f81 --- /dev/null +++ b/crudj2e/docker-compose.yml @@ -0,0 +1,23 @@ +version: '3' +services: + db: + image: mysql + restart: always + environment: + MYSQL_ROOT_PASSWORD: root123 + ports: + - 3306:3306 + container_name: mysql_docker + crudj2e: + image: crudjee:1.0.4 + build: CRUDJ2E/ + container_name: crudj2e_docker + restart: always + network_mode: host + depends_on: + - db + ports: + - 8084:8080 + expose: + - 8080 + From ef9d9bf39e5132e0769b01c29a47e259ec86a3f0 Mon Sep 17 00:00:00 2001 From: swethadevid Date: Tue, 12 Feb 2019 12:26:48 +0530 Subject: [PATCH 04/38] Registration service backend --- userservice/.gitignore | 25 +++++ userservice/Dockerfile | 5 + userservice/pom.xml | 103 ++++++++++++++++++ .../stackroute/UserserviceApplication.java | 15 +++ .../java/com/stackroute/config/Bcrypt.java | 17 +++ .../com/stackroute/config/DatabaseLoader.java | 83 ++++++++++++++ .../stackroute/config/KafkaConfiguration.java | 35 ++++++ .../stackroute/config/SwaggerDataReader.java | 23 ++++ .../stackroute/controller/UserController.java | 85 +++++++++++++++ .../exception/UserAlreadyExistsException.java | 9 ++ .../exception/UserNotFoundException.java | 10 ++ .../main/java/com/stackroute/model/User.java | 28 +++++ .../stackroute/repository/UserRepository.java | 13 +++ .../com/stackroute/service/UserService.java | 16 +++ .../stackroute/service/UserServiceImpl.java | 70 ++++++++++++ .../src/main/resources/application.properties | 10 ++ .../src/main/resources/seedData.properties | 8 ++ .../UserserviceApplicationTests.java | 17 +++ 18 files changed, 572 insertions(+) create mode 100644 userservice/.gitignore create mode 100644 userservice/Dockerfile create mode 100644 userservice/pom.xml create mode 100644 userservice/src/main/java/com/stackroute/UserserviceApplication.java create mode 100644 userservice/src/main/java/com/stackroute/config/Bcrypt.java create mode 100644 userservice/src/main/java/com/stackroute/config/DatabaseLoader.java create mode 100644 userservice/src/main/java/com/stackroute/config/KafkaConfiguration.java create mode 100644 userservice/src/main/java/com/stackroute/config/SwaggerDataReader.java create mode 100644 userservice/src/main/java/com/stackroute/controller/UserController.java create mode 100644 userservice/src/main/java/com/stackroute/exception/UserAlreadyExistsException.java create mode 100644 userservice/src/main/java/com/stackroute/exception/UserNotFoundException.java create mode 100644 userservice/src/main/java/com/stackroute/model/User.java create mode 100644 userservice/src/main/java/com/stackroute/repository/UserRepository.java create mode 100644 userservice/src/main/java/com/stackroute/service/UserService.java create mode 100644 userservice/src/main/java/com/stackroute/service/UserServiceImpl.java create mode 100644 userservice/src/main/resources/application.properties create mode 100644 userservice/src/main/resources/seedData.properties create mode 100644 userservice/src/test/java/com/stackroute/UserserviceApplicationTests.java diff --git a/userservice/.gitignore b/userservice/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/userservice/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/userservice/Dockerfile b/userservice/Dockerfile new file mode 100644 index 0000000..d92f9aa --- /dev/null +++ b/userservice/Dockerfile @@ -0,0 +1,5 @@ + +FROM openjdk:11.0-oracle +ADD ./target/userservice-0.0.1-SNAPSHOT.jar /usr/src/userservice-0.0.1-SNAPSHOT.jar +WORKDIR usr/src +ENTRYPOINT ["java","-jar","userservice-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/userservice/pom.xml b/userservice/pom.xml new file mode 100644 index 0000000..20b935b --- /dev/null +++ b/userservice/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.stackroute + userservice + 0.0.1-SNAPSHOT + userservice + Demo project for Spring Boot + + + 11 + Greenwich.RC2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-rest + + + + org.springframework.boot + spring-boot-starter-data-mongodb + 2.1.1.RELEASE + + + org.projectlombok + lombok + + + io.springfox + springfox-swagger2 + 2.6.1 + compile + + + + org.springframework.boot + spring-boot-devtools + 2.1.2.RELEASE + + + + org.springframework.kafka + spring-kafka + + + + io.springfox + springfox-swagger-ui + 2.6.1 + compile + + + org.mongodb + mongo-java-driver + 3.9.1 + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot + 2.1.2.RELEASE + + + + + org.springframework.security + spring-security-core + 5.1.2.RELEASE + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/userservice/src/main/java/com/stackroute/UserserviceApplication.java b/userservice/src/main/java/com/stackroute/UserserviceApplication.java new file mode 100644 index 0000000..0932ea2 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/UserserviceApplication.java @@ -0,0 +1,15 @@ +package com.stackroute; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class UserserviceApplication { + + public static void main(String[] args) { + SpringApplication.run(UserserviceApplication.class, args); + } + +} + diff --git a/userservice/src/main/java/com/stackroute/config/Bcrypt.java b/userservice/src/main/java/com/stackroute/config/Bcrypt.java new file mode 100644 index 0000000..928f4d7 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/config/Bcrypt.java @@ -0,0 +1,17 @@ +package com.stackroute.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class Bcrypt { + + @Bean + //for encrypting the password and store in the db using Bcrpt password encoder + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + +} diff --git a/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java b/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java new file mode 100644 index 0000000..dc3d108 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java @@ -0,0 +1,83 @@ +package com.stackroute.config; + +import com.stackroute.model.User; +import com.stackroute.repository.UserRepository; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.event.ApplicationContextEvent; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; +@Configuration +@PropertySource("classpath:seedData.properties") +@Component +public abstract class DatabaseLoader implements ApplicationListener { +//public abstract class DatabaseLoader implements ApplicationListener { + + + + @Value(("${id1}")) + private String id1; + @Value("${username1}") + private String username1; + @Value("${password1}") + private String password1; + @Value("{phonenumber1}") + private String phonenumber1; + @Value("${emailid1}") + private String emailId1; + @Value("{organisationname1}") + private String company1; + @Value("{dob1}") + private String dob1; + @Value("${role1}") + private String role1; + + + @Autowired + private UserRepository userRepository; + + @Override + public void onApplicationEvent(final ContextRefreshedEvent event){ + seedData(); + } + + private void seedData() { + User user2 = new User(); + user2.setId(id1); + user2.setUsername(username1); + user2.setPassword(password1); + user2.setPhoneNumber(phonenumber1); + user2.setEmail(emailId1); + user2.setCompany(company1); + user2.setDob(dob1); + userRepository.save(user2); + } + + + +// //Method to be over-ridden for applicationListener +// @Override +// public void onApplicationEvent(final ApplicationReadyEvent applicationReadyEvent) { +// seedData(); +// } +// +// private void seedData() { +// User user2 = new User(); +// user2.setPassword(password1); +// user2.setDob(dob1); +// user2.setPhoneNumber(phonenumber1); +// user2.setName(organisationname1); +// user2.setEmail(emailId1); +// user2.setCid(id1); +// user2.setUsername(username1); +// userRepository.save(user2); +// +// } + +} + + diff --git a/userservice/src/main/java/com/stackroute/config/KafkaConfiguration.java b/userservice/src/main/java/com/stackroute/config/KafkaConfiguration.java new file mode 100644 index 0000000..8edaa1a --- /dev/null +++ b/userservice/src/main/java/com/stackroute/config/KafkaConfiguration.java @@ -0,0 +1,35 @@ +package com.stackroute.config; + +import java.util.HashMap; +import java.util.Map; +//import com.stackroute.kafka.springbootwithkafka.model.User; +import com.stackroute.model.User; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonSerializer; + +@Configuration +//Kafka Configuration class to produce the +public class KafkaConfiguration { + @Bean + public ProducerFactory producerFactory() { + Map config = new HashMap<>(); + + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "172.23.239.85:9092"); + + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + + return new DefaultKafkaProducerFactory<>(config); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } +} diff --git a/userservice/src/main/java/com/stackroute/config/SwaggerDataReader.java b/userservice/src/main/java/com/stackroute/config/SwaggerDataReader.java new file mode 100644 index 0000000..af4b219 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/config/SwaggerDataReader.java @@ -0,0 +1,23 @@ +package com.stackroute.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerDataReader { + + @Bean + public Docket api() { + return new Docket(DocumentationType.SWAGGER_2) + .select().apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build(); + } + +} diff --git a/userservice/src/main/java/com/stackroute/controller/UserController.java b/userservice/src/main/java/com/stackroute/controller/UserController.java new file mode 100644 index 0000000..74a50de --- /dev/null +++ b/userservice/src/main/java/com/stackroute/controller/UserController.java @@ -0,0 +1,85 @@ +package com.stackroute.controller; + +import com.stackroute.model.User; +import com.stackroute.exception.UserAlreadyExistsException; +import com.stackroute.exception.UserNotFoundException; +import com.stackroute.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +@CrossOrigin("*") +@RestController +@RequestMapping("/api/v1/") +public class UserController { + + private static final String TOPIC = "Kafka_NewUser_Registration"; + private UserService userService; + @Autowired + private KafkaTemplate kafkaTemplate; + + @Autowired + public UserController(UserService userService) { + this.userService = userService; + } + +//Request mapping for posting user details + @PostMapping("register") + public ResponseEntity saveUser(@RequestBody User user) { + ResponseEntity responseEntity; + try { + userService.saveUser(user); + responseEntity = new ResponseEntity("Successfully created the User!!", HttpStatus.CREATED); + kafkaTemplate.send(TOPIC, user); + System.out.println("Published to Kafka"); + } + catch (UserAlreadyExistsException ex){ + responseEntity = new ResponseEntity(ex.getMessage(),HttpStatus.BAD_REQUEST); + //ex.printStackTrace(); + } + return responseEntity; + } + +//Request mapping for getting user details + @GetMapping("users") + public ResponseEntity> listOfUsers() { + List allUsers = userService.getAllUsers(); + return new ResponseEntity>(allUsers, HttpStatus.OK); + } + +//Request mapping for deleting user details + @DeleteMapping("users/{cid}") + public ResponseEntity deleteUser(@PathVariable("cid") String userId){ + ResponseEntity responseEntity; + try { + User user = userService.deleteUser(userId); + responseEntity = new ResponseEntity(user, HttpStatus.GONE); + } + catch (UserNotFoundException userNotFoundException){ + responseEntity = new ResponseEntity<>(userNotFoundException.getMessage(), HttpStatus.NOT_FOUND); + + } + return responseEntity; + } + //Request mapping for updating user details + @PutMapping("users/{id}") + public ResponseEntity updateUser(@PathVariable("id") String userId, @RequestBody User user) { + ResponseEntity responseEntity; + System.out.println("I am in controller"); + try{ + User user1 = userService.updateUser(userId,user); + responseEntity = new ResponseEntity(user1, HttpStatus.OK); + } + catch (UserNotFoundException ex){ + responseEntity = new ResponseEntity(ex.getMessage(),HttpStatus.NOT_FOUND); + //ex.printStackTrace(); + } + return responseEntity; + + } + + +} diff --git a/userservice/src/main/java/com/stackroute/exception/UserAlreadyExistsException.java b/userservice/src/main/java/com/stackroute/exception/UserAlreadyExistsException.java new file mode 100644 index 0000000..df62909 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/exception/UserAlreadyExistsException.java @@ -0,0 +1,9 @@ +package com.stackroute.exception; + +public class UserAlreadyExistsException extends Exception{ + final String message; + public UserAlreadyExistsException(String message){ + super(message); + this.message = message; + } +} diff --git a/userservice/src/main/java/com/stackroute/exception/UserNotFoundException.java b/userservice/src/main/java/com/stackroute/exception/UserNotFoundException.java new file mode 100644 index 0000000..a82bc77 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/exception/UserNotFoundException.java @@ -0,0 +1,10 @@ +package com.stackroute.exception; + +public class UserNotFoundException extends Exception{ + + final String message; + public UserNotFoundException(String message){ + super(message); + this.message = message; + } +} diff --git a/userservice/src/main/java/com/stackroute/model/User.java b/userservice/src/main/java/com/stackroute/model/User.java new file mode 100644 index 0000000..cae7787 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/model/User.java @@ -0,0 +1,28 @@ +package com.stackroute.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import javax.management.relation.Role; +import java.util.HashSet; +import java.util.Set; + +@Document(collection = "SyscopUser") +@Data +@NoArgsConstructor +@AllArgsConstructor + +public class User { + @Id + private String id; + private String username ; + private String password; + private String phoneNumber; + private String email; + private String company; + private String dob; + private Set roles = new HashSet<>(); +} + diff --git a/userservice/src/main/java/com/stackroute/repository/UserRepository.java b/userservice/src/main/java/com/stackroute/repository/UserRepository.java new file mode 100644 index 0000000..60b11ef --- /dev/null +++ b/userservice/src/main/java/com/stackroute/repository/UserRepository.java @@ -0,0 +1,13 @@ +package com.stackroute.repository; + +import com.stackroute.model.User; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + + +@Repository +public interface UserRepository extends MongoRepository { + +boolean existsByUsername(String username); + +} diff --git a/userservice/src/main/java/com/stackroute/service/UserService.java b/userservice/src/main/java/com/stackroute/service/UserService.java new file mode 100644 index 0000000..b56b56d --- /dev/null +++ b/userservice/src/main/java/com/stackroute/service/UserService.java @@ -0,0 +1,16 @@ +package com.stackroute.service; + +import com.stackroute.model.User; +import com.stackroute.exception.UserAlreadyExistsException; +import com.stackroute.exception.UserNotFoundException; + +import java.util.List; + +public interface UserService { + + public User saveUser (User user) throws UserAlreadyExistsException; + public List getAllUsers(); + public User deleteUser(String userId) throws UserNotFoundException; + public User updateUser(String userId, User user) throws UserNotFoundException; + } + diff --git a/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java b/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java new file mode 100644 index 0000000..da513e6 --- /dev/null +++ b/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java @@ -0,0 +1,70 @@ +package com.stackroute.service; + +import com.stackroute.model.User; +import com.stackroute.exception.UserAlreadyExistsException; +import com.stackroute.exception.UserNotFoundException; +import com.stackroute.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import java.util.List; + +@Primary +@Service +public class UserServiceImpl implements UserService { + + private UserRepository userRepository; + +// @Autowired +// private PasswordEncoder passwordEncoder; + + @Autowired + public UserServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } + + //for saving data Post + public User saveUser(User user) throws UserAlreadyExistsException { + if (userRepository.existsByUsername(user.getUsername())) { + + throw new UserAlreadyExistsException("User already exists"); + } + //Encrypting password + // user.setPassword(passwordEncoder.encode(user.getPassword())); + User user1 = userRepository.save(user); + if (user1 == null) { + throw new UserAlreadyExistsException("User already exists!!try to register new user"); + } + return user1; + } + + //for getting data Get + public List getAllUsers() { + List userList = (List) userRepository.findAll(); + return userList; + } + + //for deleting the user Delete + public User deleteUser(String userId) throws UserNotFoundException { + User user1 = userRepository.findById(userId).get(); + if (userRepository.existsById(userId)) + userRepository.deleteById(userId); + else { + throw new UserNotFoundException("User not found to delete the user"); + } + return user1; + + } + + //for updating the user Put + public User updateUser(String userId, User user) throws UserNotFoundException { + if (userRepository.existsById(userId)) { + User user1 = userRepository.save(user); + return user1; + } else + throw new UserNotFoundException("User not found to update"); + + } +} + + diff --git a/userservice/src/main/resources/application.properties b/userservice/src/main/resources/application.properties new file mode 100644 index 0000000..98bda9b --- /dev/null +++ b/userservice/src/main/resources/application.properties @@ -0,0 +1,10 @@ +server.port=8096 + +#spring.data.mongodb.host=172.23.239.170 +spring.datasource.driver-class-name=mongodb.jdbc.MongoDriver +spring.data.mongodb.host=localhost +spring.data.mongodb.port=27017 +spring.data.mongodb.database=syscopDB +spring.data.mongodb.repositories.enabled=true + + diff --git a/userservice/src/main/resources/seedData.properties b/userservice/src/main/resources/seedData.properties new file mode 100644 index 0000000..217e4d1 --- /dev/null +++ b/userservice/src/main/resources/seedData.properties @@ -0,0 +1,8 @@ +id1=1 +username1=Swetha Devi D +password1=swetha18boeing +phonenumber1=9909090900 +emailid1=swetha96devi@gmail.com +company1=BOEING +dob1=29/01/96 +role1=swetha diff --git a/userservice/src/test/java/com/stackroute/UserserviceApplicationTests.java b/userservice/src/test/java/com/stackroute/UserserviceApplicationTests.java new file mode 100644 index 0000000..89d8dd7 --- /dev/null +++ b/userservice/src/test/java/com/stackroute/UserserviceApplicationTests.java @@ -0,0 +1,17 @@ +//package com.stackroute; +// +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.test.context.junit4.SpringRunner; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest +//public class UserserviceApplicationTests { +// +// @Test +// public void contextLoads() { +// } +// +//} +// From 7316f69abd0ca9469ede81b2e05d8c5accc33b44 Mon Sep 17 00:00:00 2001 From: sushantanshu Date: Tue, 12 Feb 2019 14:25:17 +0530 Subject: [PATCH 05/38] front-end --- frontend-syscop/.gitignore | 44 + frontend-syscop/Dockerfile | 25 + frontend-syscop/README.md | 2 +- frontend-syscop/angular.json | 18 +- frontend-syscop/e2e/src/app.e2e-spec.ts | 2 +- frontend-syscop/package-lock.json | 11346 ++++++++++++++++ frontend-syscop/package.json | 9 +- frontend-syscop/src/app/app-routing.module.ts | 13 +- frontend-syscop/src/app/app.component.html | 22 +- frontend-syscop/src/app/app.component.spec.ts | 6 +- frontend-syscop/src/app/app.component.ts | 2 +- frontend-syscop/src/app/app.module.ts | 49 +- .../src/app/auth/auth-interceptor.ts | 26 + .../src/app/auth/auth.service.spec.ts | 15 + frontend-syscop/src/app/auth/auth.service.ts | 34 + frontend-syscop/src/app/auth/jwt-response.ts | 6 + frontend-syscop/src/app/auth/login-info.ts | 9 + frontend-syscop/src/app/auth/signup-info.ts | 15 + .../app/auth/token-storage.service.spec.ts | 15 + .../src/app/auth/token-storage.service.ts | 52 + .../src/app/dashboard/dashboard.component.css | 0 .../app/dashboard/dashboard.component.html | 3 + .../app/dashboard/dashboard.component.spec.ts | 25 + .../src/app/dashboard/dashboard.component.ts | 33 + .../src/app/home/home.component.css | 30 + .../src/app/home/home.component.html | 46 + .../src/app/home/home.component.spec.ts | 42 + .../src/app/home/home.component.ts | 22 + .../initial-view/initial-view.component.css | 39 + .../initial-view/initial-view.component.html | 20 + .../initial-view.component.spec.ts | 25 + .../initial-view/initial-view.component.ts | 15 + .../src/app/login/login.component.css | 26 + .../src/app/login/login.component.html | 72 + .../src/app/login/login.component.spec.ts | 25 + .../src/app/login/login.component.ts | 96 + .../src/app/signup/signup.component.css | 19 + .../src/app/signup/signup.component.html | 76 + .../src/app/signup/signup.component.spec.ts | 25 + .../src/app/signup/signup.component.ts | 34 + frontend-syscop/src/app/user.service.spec.ts | 12 + frontend-syscop/src/app/user.service.ts | 37 + frontend-syscop/src/index.html | 3 +- frontend-syscop/src/styles.css | 4 + 44 files changed, 12396 insertions(+), 43 deletions(-) create mode 100644 frontend-syscop/.gitignore create mode 100644 frontend-syscop/Dockerfile create mode 100644 frontend-syscop/package-lock.json create mode 100644 frontend-syscop/src/app/auth/auth-interceptor.ts create mode 100644 frontend-syscop/src/app/auth/auth.service.spec.ts create mode 100644 frontend-syscop/src/app/auth/auth.service.ts create mode 100644 frontend-syscop/src/app/auth/jwt-response.ts create mode 100644 frontend-syscop/src/app/auth/login-info.ts create mode 100644 frontend-syscop/src/app/auth/signup-info.ts create mode 100644 frontend-syscop/src/app/auth/token-storage.service.spec.ts create mode 100644 frontend-syscop/src/app/auth/token-storage.service.ts create mode 100644 frontend-syscop/src/app/dashboard/dashboard.component.css create mode 100644 frontend-syscop/src/app/dashboard/dashboard.component.html create mode 100644 frontend-syscop/src/app/dashboard/dashboard.component.spec.ts create mode 100644 frontend-syscop/src/app/dashboard/dashboard.component.ts create mode 100644 frontend-syscop/src/app/home/home.component.css create mode 100644 frontend-syscop/src/app/home/home.component.html create mode 100644 frontend-syscop/src/app/home/home.component.spec.ts create mode 100644 frontend-syscop/src/app/home/home.component.ts create mode 100644 frontend-syscop/src/app/initial-view/initial-view.component.css create mode 100644 frontend-syscop/src/app/initial-view/initial-view.component.html create mode 100644 frontend-syscop/src/app/initial-view/initial-view.component.spec.ts create mode 100644 frontend-syscop/src/app/initial-view/initial-view.component.ts create mode 100644 frontend-syscop/src/app/login/login.component.css create mode 100644 frontend-syscop/src/app/login/login.component.html create mode 100644 frontend-syscop/src/app/login/login.component.spec.ts create mode 100644 frontend-syscop/src/app/login/login.component.ts create mode 100644 frontend-syscop/src/app/signup/signup.component.css create mode 100644 frontend-syscop/src/app/signup/signup.component.html create mode 100644 frontend-syscop/src/app/signup/signup.component.spec.ts create mode 100644 frontend-syscop/src/app/signup/signup.component.ts create mode 100644 frontend-syscop/src/app/user.service.spec.ts create mode 100644 frontend-syscop/src/app/user.service.ts diff --git a/frontend-syscop/.gitignore b/frontend-syscop/.gitignore new file mode 100644 index 0000000..47765ad --- /dev/null +++ b/frontend-syscop/.gitignore @@ -0,0 +1,44 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules + +# profiling files +chrome-profiler-events.json +speed-measure-plugin.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git a/frontend-syscop/Dockerfile b/frontend-syscop/Dockerfile new file mode 100644 index 0000000..6c6389b --- /dev/null +++ b/frontend-syscop/Dockerfile @@ -0,0 +1,25 @@ +# base image +FROM node:9.6.1 + +# install chrome for protractor tests +RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - +RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' +RUN apt-get update && apt-get install -yq google-chrome-stable --fix-missing + +# set working directory +RUN mkdir register-front +WORKDIR /register-front + +# add `/usr/src/app/node_modules/.bin` to $PATH +ENV PATH /node_modules/.bin:$PATH + +# install and cache app dependencies +COPY package.json /package.json +RUN npm install +RUN npm install -g @angular/cli@1.7.1 + +# add app +COPY . /register-front + +# start app +CMD ng serve --host 0.0.0.0 \ No newline at end of file diff --git a/frontend-syscop/README.md b/frontend-syscop/README.md index 5354401..9103baf 100644 --- a/frontend-syscop/README.md +++ b/frontend-syscop/README.md @@ -1,4 +1,4 @@ -# FrontendSyscop +# RegisterFront This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.3. diff --git a/frontend-syscop/angular.json b/frontend-syscop/angular.json index 62a7984..f26a4ed 100644 --- a/frontend-syscop/angular.json +++ b/frontend-syscop/angular.json @@ -3,7 +3,7 @@ "version": 1, "newProjectRoot": "projects", "projects": { - "frontend-syscop": { + "register-front": { "root": "", "sourceRoot": "src", "projectType": "application", @@ -13,7 +13,7 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { - "outputPath": "dist/frontend-syscop", + "outputPath": "dist/register-front", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", @@ -57,18 +57,18 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "browserTarget": "frontend-syscop:build" + "browserTarget": "register-front:build" }, "configurations": { "production": { - "browserTarget": "frontend-syscop:build:production" + "browserTarget": "register-front:build:production" } } }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", "options": { - "browserTarget": "frontend-syscop:build" + "browserTarget": "register-front:build" } }, "test": { @@ -102,7 +102,7 @@ } } }, - "frontend-syscop-e2e": { + "register-front-e2e": { "root": "e2e/", "projectType": "application", "prefix": "", @@ -111,11 +111,11 @@ "builder": "@angular-devkit/build-angular:protractor", "options": { "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "frontend-syscop:serve" + "devServerTarget": "register-front:serve" }, "configurations": { "production": { - "devServerTarget": "frontend-syscop:serve:production" + "devServerTarget": "register-front:serve:production" } } }, @@ -131,5 +131,5 @@ } } }, - "defaultProject": "frontend-syscop" + "defaultProject": "register-front" } \ No newline at end of file diff --git a/frontend-syscop/e2e/src/app.e2e-spec.ts b/frontend-syscop/e2e/src/app.e2e-spec.ts index ccd7adb..71693a4 100644 --- a/frontend-syscop/e2e/src/app.e2e-spec.ts +++ b/frontend-syscop/e2e/src/app.e2e-spec.ts @@ -10,7 +10,7 @@ describe('workspace-project App', () => { it('should display welcome message', () => { page.navigateTo(); - expect(page.getTitleText()).toEqual('Welcome to frontend-syscop!'); + expect(page.getTitleText()).toEqual('Welcome to register-front!'); }); afterEach(async () => { diff --git a/frontend-syscop/package-lock.json b/frontend-syscop/package-lock.json new file mode 100644 index 0000000..aa646ba --- /dev/null +++ b/frontend-syscop/package-lock.json @@ -0,0 +1,11346 @@ +{ + "name": "register-front", + "version": "0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.12.4.tgz", + "integrity": "sha512-19f3jbGyP+GzTSBgrHR4SWGK81SUgtTxhkAcyhmsIEDuXrMJM8kPu0HB9WivJ5p08+jzwz6xdF9mpNYSeD9uqw==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.2.4", + "rxjs": "6.3.3" + } + }, + "@angular-devkit/build-angular": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.12.4.tgz", + "integrity": "sha512-zzjpM9GpCGEUtTPv/T04CALzFXkTgAAiNt1whY1Vmtu2YYUksXOm1ysA2RBLEhb81RodTEwVV2zFUj5v2xHYEw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.12.4", + "@angular-devkit/build-optimizer": "0.12.4", + "@angular-devkit/build-webpack": "0.12.4", + "@angular-devkit/core": "7.2.4", + "@ngtools/webpack": "7.2.4", + "ajv": "6.6.2", + "autoprefixer": "9.4.3", + "circular-dependency-plugin": "5.0.2", + "clean-css": "4.2.1", + "copy-webpack-plugin": "4.6.0", + "file-loader": "2.0.0", + "glob": "7.1.3", + "istanbul": "0.4.5", + "istanbul-instrumenter-loader": "3.0.1", + "karma-source-map-support": "1.3.0", + "less": "3.9.0", + "less-loader": "4.1.0", + "license-webpack-plugin": "2.0.4", + "loader-utils": "1.1.0", + "mini-css-extract-plugin": "0.4.4", + "minimatch": "3.0.4", + "node-sass": "4.10.0", + "opn": "5.4.0", + "parse5": "4.0.0", + "portfinder": "1.0.17", + "postcss": "7.0.13", + "postcss-import": "12.0.1", + "postcss-loader": "3.0.0", + "raw-loader": "0.5.1", + "rxjs": "6.3.3", + "sass-loader": "7.1.0", + "semver": "5.5.1", + "source-map-loader": "0.2.4", + "source-map-support": "0.5.9", + "speed-measure-webpack-plugin": "1.2.5", + "stats-webpack-plugin": "0.7.0", + "style-loader": "0.23.1", + "stylus": "0.54.5", + "stylus-loader": "3.0.2", + "terser-webpack-plugin": "1.2.1", + "tree-kill": "1.2.0", + "webpack": "4.28.4", + "webpack-dev-middleware": "3.4.0", + "webpack-dev-server": "3.1.14", + "webpack-merge": "4.1.4", + "webpack-sources": "1.3.0", + "webpack-subresource-integrity": "1.1.0-rc.6" + } + }, + "@angular-devkit/build-optimizer": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.12.4.tgz", + "integrity": "sha512-KraU+ZARX7JMtttPjOku9wVF+dnjMsIbiIVsQrNXhpFiGT1fSJhQTPxc98ONgEmUiGROFXXq2mHLilvMr2WdwQ==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "source-map": "0.5.6", + "typescript": "3.2.4", + "webpack-sources": "1.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "webpack-sources": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", + "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.12.4.tgz", + "integrity": "sha512-1+t2MUB+dq+1LbfTnvzZwj2QTWiugyMywXqYjsyt0rrh7VcriD1lQ+P5yN8kgFz/R7Ut4LgvS05yDX1JHi20qw==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.12.4", + "@angular-devkit/core": "7.2.4", + "rxjs": "6.3.3" + } + }, + "@angular-devkit/core": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-7.2.4.tgz", + "integrity": "sha512-XHF59tIHg2qEM1Wd415xhykBLjjfOK6yMB7CjNk1bToUMX2QDT87izJF4y1Vwa0lIw9G0jdgP/4/M/OqXcbYmA==", + "dev": true, + "requires": { + "ajv": "6.6.2", + "chokidar": "2.0.4", + "fast-json-stable-stringify": "2.0.0", + "rxjs": "6.3.3", + "source-map": "0.7.3" + } + }, + "@angular-devkit/schematics": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-7.2.4.tgz", + "integrity": "sha512-ObIDnIxXRpts+Jzs0PQ7JVuK4d5vWEh9K+Ow8nMO5/LmYJQ8/2nMEQo/9lhdKPMiXmhbuvB7qZL5J+cxwwijhw==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.2.4", + "rxjs": "6.3.3" + } + }, + "@angular/animations": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-7.2.4.tgz", + "integrity": "sha512-Wx6cqU6koFOASlyl4aCygtbtROoehU6OKwV2EZTkfzHx6Eu/QyTiSa5kyoApVM5LMmCNeb8SxJMSAnKXztNl0A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/cdk": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-7.3.1.tgz", + "integrity": "sha512-m6hy+HPLDgVM+Y2C3UWvIG06euIV8oG5QxrCitZui8JZ/dj/0V5bxATKgWN62haYnYR4en4z1bfH0kOvQj+LEA==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^1.7.1" + }, + "dependencies": { + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "optional": true + } + } + }, + "@angular/cli": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-7.2.4.tgz", + "integrity": "sha512-xv3e0JE4bLZA7GAOBWB9F2Bxq5M9Zw/Agl8Q9mNiPVnYVD3ZeFNfrAat43CxIL1co0W1YEvVzadsCR8kbRmL9A==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.12.4", + "@angular-devkit/core": "7.2.4", + "@angular-devkit/schematics": "7.2.4", + "@schematics/angular": "7.2.4", + "@schematics/update": "0.12.4", + "inquirer": "6.2.1", + "opn": "5.3.0", + "semver": "5.5.1", + "symbol-observable": "1.2.0" + }, + "dependencies": { + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + } + } + }, + "@angular/common": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-7.2.3.tgz", + "integrity": "sha512-VZOTZdvkitaKEhkxL6daHxPcKqAFwNJm0U4NFB4LRP9KspsFTE60QFVB63o129PTIH9iOQ2D3HRKSRl4o78ZKg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-7.2.3.tgz", + "integrity": "sha512-UM6n4MyZkR5+VVjlwhLH8IfqdWBkdFcF5at4ckJXOJ/gkIUq97irbis9pGj1b0TO7MAl8uhF4b68xe5lk8b49g==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/compiler-cli": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-7.2.3.tgz", + "integrity": "sha512-31hcfTrU2GW66cvvaS629dNVPfiUrUWPncI28optvmKHBaH0mFqkdYNgabuslsXZV5AeidKMUJvR7GITjtvkQA==", + "dev": true, + "requires": { + "canonical-path": "1.0.0", + "chokidar": "^1.4.2", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.7.2", + "magic-string": "^0.25.0", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "shelljs": "^0.8.1", + "source-map": "^0.6.1", + "tslib": "^1.9.0", + "yargs": "9.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1" + } + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", + "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "dev": true, + "requires": { + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chokidar": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", + "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "dev": true, + "requires": { + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "expand-brackets": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", + "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", + "dev": true, + "requires": { + "is-posix-bracket": "^0.1.0" + } + }, + "extglob": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", + "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "micromatch": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", + "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", + "dev": true, + "requires": { + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-9.0.1.tgz", + "integrity": "sha1-UqzCP+7Kw0BCB47njAwAf1CF20w=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "@angular/core": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-7.2.3.tgz", + "integrity": "sha512-6Ql+sJJnrsxh8O0/IgIP1GgT4eLOHk+dlBs7zBbjstmLuhaQdY+awO9WKoQow+TiD1Go7FW1J3vZ2PTWXKxqjQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/flex-layout": { + "version": "7.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-7.0.0-beta.23.tgz", + "integrity": "sha512-jH2i3i/M7SbK6scVlj2urVL5OhzwavbQ7KwvUjyc/UwccKnnzuOuWEGCINLja/aoaUO3I35LluCLv6a6VN0olA==", + "requires": { + "tslib": "^1.7.1" + } + }, + "@angular/forms": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-7.2.3.tgz", + "integrity": "sha512-mZpyonfSmRwSvM6efvwFwkLJkK6wHQrm7X4OhVVu3s9i7BI253eLDY7WIRXFvoxJ/5jWIIarVnd/9UA7GINZGw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/language-service": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-7.2.3.tgz", + "integrity": "sha512-9FBVYbKaNx4Ap+Suz/2ZFBPca1voinZMOCN8LjXRYnfS2MHLQASQlTlK4qeZcomyRfy0FxWmO9R02S7YJ06cnw==", + "dev": true + }, + "@angular/material": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-7.3.1.tgz", + "integrity": "sha512-txq2y/JSB+05l8a/OfnByw7AEKSeAWAJ49jzCoeb3804sCeVEbvuWZfpmKBX8vCVTZh005Ypuk55R4NP5AIgaw==", + "requires": { + "tslib": "^1.7.1" + } + }, + "@angular/platform-browser": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-7.2.3.tgz", + "integrity": "sha512-DH0Y2lgEgcrP1I/DUQB/krL7Ob7yL685fu4sRapW17SndTQa2pqSFMBVf+mN3FupTXp7nJHSvlIktzedIk04+g==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-7.2.3.tgz", + "integrity": "sha512-M8Kiz5FUhnFybJuk/mgOhBjVbRgKDC4bGWKWH9Z9SXBR2dS/FL3QOJsLIthQcWlHOzSoJdEoPBRhn0R4pyLBSw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@angular/router": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-7.2.3.tgz", + "integrity": "sha512-SH7H2I9WTj1puei4m4g5n0/Cp28HS14q4r8lOgW0gLWuT6Ls7MqH/nDjOMiW924iRR6zjQQs7G+WbhL1jmZc2A==", + "requires": { + "tslib": "^1.9.0" + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/generator": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.2.tgz", + "integrity": "sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ==", + "dev": true, + "requires": { + "@babel/types": "^7.3.2", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz", + "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.2.tgz", + "integrity": "sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ==", + "dev": true + }, + "@babel/template": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz", + "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.2.2", + "@babel/types": "^7.2.2" + } + }, + "@babel/traverse": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz", + "integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.2.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.0.0", + "@babel/parser": "^7.2.3", + "@babel/types": "^7.2.2", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz", + "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.2.tgz", + "integrity": "sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@ngtools/webpack": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-7.2.4.tgz", + "integrity": "sha512-mXMDODmy53kk+Kb5jgLNQOsSrDBQQMf6C6KZNuGo8AdvUGdGaQeZDze4o7bcUz1KUjuaaP1Zh7pZtho8C4/T+Q==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.2.4", + "enhanced-resolve": "4.1.0", + "rxjs": "6.3.3", + "tree-kill": "1.2.0", + "webpack-sources": "1.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "webpack-sources": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.2.0.tgz", + "integrity": "sha512-9BZwxR85dNsjWz3blyxdOhTgtnQvv3OEs5xofI0wPYTwu5kaWxS08UuD1oI7WLBLpRO+ylf0ofnXLXWmGb2WMw==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + } + } + }, + "@schematics/angular": { + "version": "7.2.4", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-7.2.4.tgz", + "integrity": "sha512-aflQwIX4E9tDhp6ZASuQCm8CzxLxdkuOe6qN1FbCxpxMUc9E+iK9jhOjw+Xnl3boJpWHAA+k9JO1sYe3wrh3Ng==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.2.4", + "@angular-devkit/schematics": "7.2.4", + "typescript": "3.2.2" + }, + "dependencies": { + "typescript": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz", + "integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==", + "dev": true + } + } + }, + "@schematics/update": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.12.4.tgz", + "integrity": "sha512-VJbRGt+WipRdsbFdW40DASIqy8r4o40k2Umv7A0/cjFkq1egyZSC8QAYXhWx3iYn7BSXfGg83hknaAhYCC6Yjw==", + "dev": true, + "requires": { + "@angular-devkit/core": "7.2.4", + "@angular-devkit/schematics": "7.2.4", + "@yarnpkg/lockfile": "1.1.0", + "ini": "1.3.5", + "pacote": "9.1.1", + "rxjs": "6.3.3", + "semver": "5.5.1", + "semver-intersect": "1.4.0" + } + }, + "@types/jasmine": { + "version": "2.8.16", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.16.tgz", + "integrity": "sha512-056oRlBBp7MDzr+HoU5su099s/s7wjZ3KcHxLfv+Byqb9MwdLUvsfLgw1VS97hsh3ddxSPyQu+olHMnoVTUY6g==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.6.tgz", + "integrity": "sha512-2ZOKrxb8bKRmP/po5ObYnRDgFE4i+lQiEB27bAMmtMWLgJSqlIDqlLx6S0IRorpOmOPRQ6O80NujTmQAtBkeNw==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/node": { + "version": "8.9.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz", + "integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.14.tgz", + "integrity": "sha512-4GbNCDs98uHCT/OMv40qQC/OpoPbYn9XdXeTiFwHBBFO6eJhYEPUu2zDKirXSbHlvDV8oZ9l8EQ+HrEx/YS9DQ==", + "dev": true + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.5.tgz", + "integrity": "sha512-zfvjpp7jiafSmrzJ2/i3LqOyTYTuJ7u1KOXlKgDlvsj9Rr0x7ZiYu5lZbXwobL7lmsRNtPXlBfmaUD8eU2Hu8w==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@webassemblyjs/ast": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", + "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/wast-parser": "1.7.11" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", + "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", + "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", + "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", + "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.7.11" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", + "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", + "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", + "dev": true + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", + "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", + "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", + "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", + "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.1" + } + }, + "@webassemblyjs/utf8": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", + "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", + "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/helper-wasm-section": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11", + "@webassemblyjs/wasm-opt": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", + "@webassemblyjs/wast-printer": "1.7.11" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", + "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/ieee754": "1.7.11", + "@webassemblyjs/leb128": "1.7.11", + "@webassemblyjs/utf8": "1.7.11" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", + "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", + "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-api-error": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/ieee754": "1.7.11", + "@webassemblyjs/leb128": "1.7.11", + "@webassemblyjs/utf8": "1.7.11" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", + "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/floating-point-hex-parser": "1.7.11", + "@webassemblyjs/helper-api-error": "1.7.11", + "@webassemblyjs/helper-code-frame": "1.7.11", + "@webassemblyjs/helper-fsm": "1.7.11", + "@xtuc/long": "4.2.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", + "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/wast-parser": "1.7.11", + "@xtuc/long": "4.2.1" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", + "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", + "dev": true + }, + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", + "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "dev": true, + "requires": { + "acorn": "^5.0.0" + } + }, + "adm-zip": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.13.tgz", + "integrity": "sha512-fERNJX8sOXfel6qCBCMPvZLzENBEhZTzKqg6vrOW5pvoEaQuJhRU4ndTAh6lHOxn1I6jnz2NHra56ZODM751uw==", + "dev": true + }, + "after": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", + "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=", + "dev": true + }, + "agent-base": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz", + "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", + "dev": true, + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "ajv": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.6.2.tgz", + "integrity": "sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", + "dev": true + }, + "ajv-keywords": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.3.0.tgz", + "integrity": "sha512-CMzN9S62ZOO4sA/mJZIO4S++ZM7KFWzH3PPWkveLhy4OZ9i1/VatgwWMD46w/XbGCBy7Ye0gCk+Za6mmyfKK7g==", + "dev": true + }, + "amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", + "dev": true + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "dev": true + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "app-root-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.1.0.tgz", + "integrity": "sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=", + "dev": true + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "optional": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "arraybuffer.slice": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true, + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "async-foreach": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", + "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.3.tgz", + "integrity": "sha512-/XSnzDepRkAU//xLcXA/lUWxpsBuw0WiriAHOqnxkuCtzLhaz+fL4it4gp20BQ8n5SyLzK/FOc7A0+u/rti2FQ==", + "dev": true, + "requires": { + "browserslist": "^4.3.6", + "caniuse-lite": "^1.0.30000921", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.6", + "postcss-value-parser": "^3.3.1" + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "backo2": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "base64id": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", + "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "better-assert": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", + "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", + "dev": true, + "requires": { + "callsite": "1.0.0" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==", + "dev": true + }, + "blob": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", + "dev": true + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "optional": true, + "requires": { + "inherits": "~2.0.0" + } + }, + "blocking-proxy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", + "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "bluebird": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", + "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz", + "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000929", + "electron-to-chromium": "^1.3.103", + "node-releases": "^1.1.3" + } + }, + "browserstack": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.5.2.tgz", + "integrity": "sha512-+6AFt9HzhKykcPF79W6yjEUJcdvZOV0lIXdkORXMJftGrDl0OKWqRF4GHqpDNkxiceDT/uB7Fb/aDwktvXX7dg==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=", + "dev": true + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cacache": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "dev": true, + "requires": { + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "optional": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30000935", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000935.tgz", + "integrity": "sha512-1Y2uJ5y56qDt3jsDTdBHL1OqiImzjoQcBG6Yl3Qizq8mcc2SgCFpi+ZwLLqkztYnk9l87IYqRlNBnPSOTbFkXQ==", + "dev": true + }, + "canonical-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz", + "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", + "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.2.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "lodash.debounce": "^4.0.8", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.5" + } + }, + "chownr": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", + "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "circular-dependency-plugin": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.0.2.tgz", + "integrity": "sha512-oC7/DVAyfcY3UWKm0sN/oVoDedQDQiw/vIiAnuTWTpE5s0zWf7l3WY417Xw/Fbi/QbAjctAkxgMiS9P0s3zkmA==", + "dev": true + }, + "circular-json": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", + "dev": true + }, + "clone-deep": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", + "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "codelyzer": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-4.5.0.tgz", + "integrity": "sha512-oO6vCkjqsVrEsmh58oNlnJkRXuA30hF8cdNAQV9DytEalDwyOFRvHMnlKFzmOStNerOmPGZU9GAHnBo4tGvtiQ==", + "dev": true, + "requires": { + "app-root-path": "^2.1.0", + "css-selector-tokenizer": "^0.7.0", + "cssauron": "^1.4.0", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "combine-lists": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/combine-lists/-/combine-lists-1.0.1.tgz", + "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", + "dev": true, + "requires": { + "lodash": "^4.5.0" + } + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-versions": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz", + "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=", + "dev": true + }, + "compressible": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", + "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", + "dev": true, + "requires": { + "mime-db": ">= 1.36.0 < 2" + } + }, + "compression": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", + "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.14", + "debug": "2.6.9", + "on-headers": "~1.0.1", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "dependencies": { + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", + "dev": true, + "requires": { + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" + } + }, + "core-js": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.3.tgz", + "integrity": "sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-4.0.0.tgz", + "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "optional": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-parse": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.7.0.tgz", + "integrity": "sha1-Mh9s9zeCpv91ERE5D8BeLGV9jJs=", + "dev": true + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + } + }, + "cssauron": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz", + "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", + "dev": true, + "requires": { + "through": "X.X.X" + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "optional": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", + "dev": true + }, + "cyclist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", + "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "dev": true + }, + "d3": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.9.1.tgz", + "integrity": "sha512-JceuBn5VVWySPQc9EA0gfq0xQVgEQXGokHhe+359bmgGeUITLK2r2b9idMzquQne9DKxb7JDCE1gDRXe9OIF2Q==", + "requires": { + "d3-array": "1", + "d3-axis": "1", + "d3-brush": "1", + "d3-chord": "1", + "d3-collection": "1", + "d3-color": "1", + "d3-contour": "1", + "d3-dispatch": "1", + "d3-drag": "1", + "d3-dsv": "1", + "d3-ease": "1", + "d3-fetch": "1", + "d3-force": "1", + "d3-format": "1", + "d3-geo": "1", + "d3-hierarchy": "1", + "d3-interpolate": "1", + "d3-path": "1", + "d3-polygon": "1", + "d3-quadtree": "1", + "d3-random": "1", + "d3-scale": "2", + "d3-scale-chromatic": "1", + "d3-selection": "1", + "d3-shape": "1", + "d3-time": "1", + "d3-time-format": "2", + "d3-timer": "1", + "d3-transition": "1", + "d3-voronoi": "1", + "d3-zoom": "1" + } + }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + }, + "d3-brush": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz", + "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "requires": { + "d3-array": "1", + "d3-path": "1" + } + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz", + "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==" + }, + "d3-contour": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", + "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "requires": { + "d3-array": "^1.1.1" + } + }, + "d3-dispatch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", + "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" + }, + "d3-drag": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz", + "integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-dsv": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz", + "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==", + "requires": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + } + }, + "d3-ease": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz", + "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==" + }, + "d3-fetch": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz", + "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==", + "requires": { + "d3-dsv": "1" + } + }, + "d3-force": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.0.tgz", + "integrity": "sha512-PFLcDnRVANHMudbQlIB87gcfQorEsDIAvRpZ2bNddfM/WxdsEkyrEaOIPoydhH1I1V4HPjNLGOMLXCA0AuGQ9w==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", + "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" + }, + "d3-geo": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.3.tgz", + "integrity": "sha512-n30yN9qSKREvV2fxcrhmHUdXP9TNH7ZZj3C/qnaoU0cVf/Ea85+yT7HY7i8ySPwkwjCNYtmKqQFTvLFngfkItQ==", + "requires": { + "d3-array": "1" + } + }, + "d3-hierarchy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", + "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==" + }, + "d3-interpolate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", + "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", + "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==" + }, + "d3-polygon": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz", + "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w==" + }, + "d3-quadtree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz", + "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==" + }, + "d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + }, + "d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "d3-scale-chromatic": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz", + "integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==", + "requires": { + "d3-color": "1", + "d3-interpolate": "1" + } + }, + "d3-selection": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz", + "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==" + }, + "d3-shape": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.4.tgz", + "integrity": "sha512-izaz4fOpOnY3CD17hkZWNxbaN70sIGagLR/5jb6RS96Y+6VqX+q1BQf1av6QSBRdfULi3Gb8Js4CzG4+KAPjMg==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz", + "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==" + }, + "d3-time-format": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", + "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" + }, + "d3-transition": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz", + "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, + "d3-zoom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.3.tgz", + "integrity": "sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "date-format": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", + "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", + "dev": true + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-gateway": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", + "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "dev": true, + "requires": { + "execa": "^0.10.0", + "ip-regex": "^2.1.0" + } + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "dependencies": { + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "dependency-graph": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz", + "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.113", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz", + "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==", + "dev": true + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "dev": true, + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "1.0.0", + "cookie": "0.3.1", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "ws": "~3.3.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-client": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "engine.io-parser": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", + "dev": true, + "requires": { + "after": "0.8.2", + "arraybuffer.slice": "~0.0.7", + "base64-arraybuffer": "0.1.5", + "blob": "0.0.5", + "has-binary2": "~1.0.2" + } + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "err-code": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz", + "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es6-promise": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", + "dev": true + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "dev": true, + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" + }, + "dependencies": { + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "dev": true + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "dev": true, + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-braces": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/expand-braces/-/expand-braces-0.1.2.tgz", + "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", + "dev": true, + "requires": { + "array-slice": "^0.2.3", + "array-unique": "^0.2.1", + "braces": "^0.1.2" + }, + "dependencies": { + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "braces": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/braces/-/braces-0.1.5.tgz", + "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", + "dev": true, + "requires": { + "expand-range": "^0.1.0" + } + }, + "expand-range": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz", + "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", + "dev": true, + "requires": { + "is-number": "^0.1.1", + "repeat-string": "^0.2.2" + } + }, + "is-number": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-0.1.1.tgz", + "integrity": "sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=", + "dev": true + }, + "repeat-string": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-0.2.2.tgz", + "integrity": "sha1-x6jTI2BoNiBZp+RlH8aITosftK4=", + "dev": true + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-range": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", + "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", + "dev": true, + "requires": { + "fill-range": "^2.1.0" + }, + "dependencies": { + "fill-range": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", + "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "dev": true, + "requires": { + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" + } + }, + "is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz", + "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^1.0.0" + } + }, + "filename-regex": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", + "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", + "dev": true + }, + "fileset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz", + "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", + "dev": true, + "requires": { + "glob": "^7.0.3", + "minimatch": "^3.0.3" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.0.tgz", + "integrity": "sha512-6MHED/cmsyux1G4/Cek2Z776y9t7WCNd3h2h/HW91vFeU7pzMhA8XvAlDhHcanG5IWuIh/xcC7JASY4WQpG6xg==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "follow-redirects": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.6.1.tgz", + "integrity": "sha512-t2JCjbzxQpWvbhts3l6SH1DKzSrx8a+SsaVf4h6bG4kOXUuPYS/kg2Lr4gQSb7eemaHqJkOThF1BGyjlUkO1GQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", + "dev": true, + "requires": { + "null-check": "^1.0.0" + } + }, + "fs-minipass": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "gaze": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", + "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", + "dev": true, + "optional": true, + "requires": { + "globule": "^1.0.0" + } + }, + "genfun": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz", + "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "optional": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-base": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", + "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", + "dev": true, + "requires": { + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", + "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", + "dev": true, + "requires": { + "is-glob": "^2.0.0" + } + }, + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "globule": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", + "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", + "dev": true, + "optional": true, + "requires": { + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "dev": true + }, + "handlebars": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", + "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==", + "dev": true, + "requires": { + "async": "^2.5.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-binary2": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", + "dev": true, + "requires": { + "isarray": "2.0.1" + }, + "dependencies": { + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "has-cors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", + "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "dev": true + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "requires": { + "agent-base": "4", + "debug": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "dev": true, + "requires": { + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz", + "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", + "dev": true, + "requires": { + "agent-base": "^4.1.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", + "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "in-publish": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", + "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", + "dev": true, + "optional": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "optional": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.1.tgz", + "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.0", + "figures": "^2.0.0", + "lodash": "^4.17.10", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.1.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz", + "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", + "dev": true, + "requires": { + "ansi-regex": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz", + "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==", + "dev": true + } + } + } + } + }, + "internal-ip": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", + "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", + "dev": true, + "requires": { + "default-gateway": "^2.6.0", + "ipaddr.js": "^1.5.2" + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", + "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-builtin-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", + "dev": true, + "requires": { + "builtin-modules": "^1.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-dotfile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", + "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", + "dev": true + }, + "is-equal-shallow": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", + "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", + "dev": true, + "requires": { + "is-primitive": "^2.0.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-posix-bracket": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", + "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", + "dev": true + }, + "is-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", + "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", + "dev": true + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true, + "optional": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isbinaryfile": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", + "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", + "dev": true, + "requires": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "dependencies": { + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "istanbul-api": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.0.tgz", + "integrity": "sha512-+Ygg4t1StoiNlBGc6x0f8q/Bv26FbZqP/+jegzfNpU7Q8o+4ZRoJxJPhBkgE/UonpAjtxnE4zCZIyJX+MwLRMQ==", + "dev": true, + "requires": { + "async": "^2.6.1", + "compare-versions": "^3.2.1", + "fileset": "^2.0.3", + "istanbul-lib-coverage": "^2.0.3", + "istanbul-lib-hook": "^2.0.3", + "istanbul-lib-instrument": "^3.1.0", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.2", + "istanbul-reports": "^2.1.0", + "js-yaml": "^3.12.0", + "make-dir": "^1.3.0", + "minimatch": "^3.0.4", + "once": "^1.4.0" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz", + "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==", + "dev": true, + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "istanbul-lib-coverage": "^2.0.3", + "semver": "^5.5.0" + } + } + } + }, + "istanbul-instrumenter-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.1.tgz", + "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "istanbul-lib-instrument": "^1.7.3", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "schema-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", + "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", + "dev": true, + "requires": { + "ajv": "^5.0.0" + } + } + } + }, + "istanbul-lib-coverage": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz", + "integrity": "sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz", + "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz", + "integrity": "sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==", + "dev": true, + "requires": { + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.1", + "semver": "^5.3.0" + } + }, + "istanbul-lib-report": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz", + "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "supports-color": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz", + "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.3", + "make-dir": "^1.3.0", + "rimraf": "^2.6.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.0.tgz", + "integrity": "sha512-azQdSX+dtTtkQEfqq20ICxWi6eOHXyHIgMFw1VOOVi8iIPWeCWRgCyFh/CsBKIhcgskMI8ExXmU7rjXTRCIJ+A==", + "dev": true, + "requires": { + "handlebars": "^4.0.11" + } + }, + "jasmine": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", + "dev": true, + "requires": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "dependencies": { + "jasmine-core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", + "dev": true + } + } + }, + "jasmine-core": { + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", + "dev": true + }, + "jasmine-spec-reporter": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz", + "integrity": "sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg==", + "dev": true, + "requires": { + "colors": "1.1.2" + } + }, + "jasminewd2": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", + "dev": true + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "dev": true, + "optional": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz", + "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", + "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", + "dev": true, + "requires": { + "core-js": "~2.3.0", + "es6-promise": "~3.0.2", + "lie": "~3.1.0", + "pako": "~1.0.2", + "readable-stream": "~2.0.6" + }, + "dependencies": { + "core-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", + "dev": true + }, + "es6-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "karma": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", + "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", + "dev": true, + "requires": { + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "chokidar": "^2.0.3", + "colors": "^1.1.0", + "combine-lists": "^1.0.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "expand-braces": "^0.1.1", + "flatted": "^2.0.0", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.5", + "log4js": "^3.0.0", + "mime": "^2.3.1", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", + "socket.io": "2.1.1", + "source-map": "^0.6.1", + "tmp": "0.0.33", + "useragent": "2.3.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz", + "integrity": "sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w==", + "dev": true, + "requires": { + "fs-access": "^1.0.0", + "which": "^1.2.1" + } + }, + "karma-coverage-istanbul-reporter": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-2.0.4.tgz", + "integrity": "sha512-xJS7QSQIVU6VK9HuJ/ieE5yynxKhjCCkd96NLY/BX/HXsx0CskU9JJiMQbd4cHALiddMwI4OWh1IIzeWrsavJw==", + "dev": true, + "requires": { + "istanbul-api": "^2.0.5", + "minimatch": "^3.0.4" + } + }, + "karma-jasmine": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.2.tgz", + "integrity": "sha1-OU8rJf+0pkS5rabyLUQ+L9CIhsM=", + "dev": true + }, + "karma-jasmine-html-reporter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", + "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", + "dev": true, + "requires": { + "karma-jasmine": "^1.0.2" + } + }, + "karma-source-map-support": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.3.0.tgz", + "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "less": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", + "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", + "dev": true, + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz", + "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "license-webpack-plugin": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.0.4.tgz", + "integrity": "sha512-FQgOqrrIcD4C/VQo6ecWgXZULK5rs0kIDJtHcSVO6SBUrD63kEHZwmKOvBTquFQSgMQn/yeH68qooKDfqiBF2Q==", + "dev": true, + "requires": { + "@types/webpack-sources": "^0.1.5", + "webpack-sources": "^1.2.0" + } + }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "optional": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", + "dev": true + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=", + "dev": true, + "optional": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.mergewith": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz", + "integrity": "sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==", + "dev": true, + "optional": true + }, + "lodash.tail": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", + "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=", + "dev": true + }, + "log4js": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", + "dev": true, + "requires": { + "circular-json": "^0.5.5", + "date-format": "^1.2.0", + "debug": "^3.1.0", + "rfdc": "^1.1.2", + "streamroller": "0.7.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "optional": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "magic-string": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.2.tgz", + "integrity": "sha512-iLs9mPjh9IuTtRsqqhNGYcZXGei0Nh/A4xirrsqW7c+QhKVFL2vm7U09ru6cHRD22azaP/wMDgI+HCqbETMTtg==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-fetch-happen": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz", + "integrity": "sha512-7R5ivfy9ilRJ1EMKIOziwrns9fGeAD4bAha8EB7BIiBBLHm2KeTUGCrICFt2rbHfzheTLynv50GnNTK1zDTrcQ==", + "dev": true, + "requires": { + "agentkeepalive": "^3.4.1", + "cacache": "^11.0.1", + "http-cache-semantics": "^3.8.1", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "mississippi": "^3.0.0", + "node-fetch-npm": "^2.0.2", + "promise-retry": "^1.1.1", + "socks-proxy-agent": "^4.0.0", + "ssri": "^6.0.0" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "optional": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-random": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", + "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "optional": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true, + "optional": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "mime-db": { + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true + }, + "mime-types": { + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, + "requires": { + "mime-db": "~1.37.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.4.tgz", + "integrity": "sha512-o+Jm+ocb0asEngdM6FsZWtZsRzA8koFUudIDwYUfl94M3PejPHG7Vopw5hN9V8WsMkSFpm3tZP3Fesz89EyrfQ==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minipass": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "minizlib": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", + "dev": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mississippi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", + "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", + "dev": true, + "requires": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-fetch-npm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz", + "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", + "dev": true, + "requires": { + "encoding": "^0.1.11", + "json-parse-better-errors": "^1.0.0", + "safe-buffer": "^5.1.1" + } + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "dev": true + }, + "node-gyp": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "dev": true, + "optional": true, + "requires": { + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true, + "optional": true + } + } + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.7.tgz", + "integrity": "sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA==", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "node-sass": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.10.0.tgz", + "integrity": "sha512-fDQJfXszw6vek63Fe/ldkYXmRYK/QS6NbvM3i5oEo9ntPDy4XX7BcKZyTKv+/kSSxRtXXc7l+MSwEmYc0CSy6Q==", + "dev": true, + "optional": true, + "requires": { + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", + "request": "^2.88.0", + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "optional": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "optional": true + } + } + }, + "nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.2.tgz", + "integrity": "sha512-YcMnjqeoUckXTPKZSAsPjUPLxH85XotbpqK3w4RyCwdFQSU5FxxBys8buehkSfg0j9fKvV1hn7O0+8reEgkAiw==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "dev": true + }, + "npm-package-arg": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", + "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", + "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", + "dev": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-2.2.3.tgz", + "integrity": "sha512-+IluBC5K201+gRU85vFlUwX3PFShZAbAgDNp2ewJdWMVSppdo/Zih0ul2Ecky/X7b51J7LrrUAP+XOmOCvYZqA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1", + "npm-package-arg": "^6.0.0", + "semver": "^5.4.1" + } + }, + "npm-registry-fetch": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-3.9.0.tgz", + "integrity": "sha512-srwmt8YhNajAoSAaDWndmZgx89lJwIZ1GWxOuckH4Coek4uHv5S+o/l9FLQe/awA+JwTnj4FJHldxhlXdZEBmw==", + "dev": true, + "requires": { + "JSONStream": "^1.3.4", + "bluebird": "^3.5.1", + "figgy-pudding": "^3.4.1", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "npm-package-arg": "^6.1.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=", + "dev": true + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-component": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", + "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.omit": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", + "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", + "dev": true, + "requires": { + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" + }, + "dependencies": { + "for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + } + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", + "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "opn": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", + "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "optional": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pacote": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.1.1.tgz", + "integrity": "sha512-f28Rq5ozzKAA9YwIKw61/ipwAatUZseYmVssDbHHaexF0wRIVotapVEZPAjOT7Eu3LYVqEp0NVpNizoAnYBUaA==", + "dev": true, + "requires": { + "bluebird": "^3.5.2", + "cacache": "^11.2.0", + "figgy-pudding": "^3.5.1", + "get-stream": "^4.1.0", + "glob": "^7.1.3", + "lru-cache": "^4.1.3", + "make-fetch-happen": "^4.0.1", + "minimatch": "^3.0.4", + "minipass": "^2.3.5", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "normalize-package-data": "^2.4.0", + "npm-package-arg": "^6.1.0", + "npm-packlist": "^1.1.12", + "npm-pick-manifest": "^2.1.0", + "npm-registry-fetch": "^3.8.0", + "osenv": "^0.1.5", + "promise-inflight": "^1.0.1", + "promise-retry": "^1.1.1", + "protoduck": "^5.0.1", + "rimraf": "^2.6.2", + "safe-buffer": "^5.1.2", + "semver": "^5.6.0", + "ssri": "^6.0.1", + "tar": "^4.4.6", + "unique-filename": "^1.1.1", + "which": "^1.3.1" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + } + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tar": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", + "dev": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "pako": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz", + "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==", + "dev": true + }, + "parallel-transform": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", + "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "dev": true, + "requires": { + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.3.tgz", + "integrity": "sha512-VrPoetlz7B/FqjBLD2f5wBVZvsZVLnRUrxVLfRYhGXCODa/NWE4p3Wp+6+aV3ZPL3KM7/OZmxDIwwijD7yuucg==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", + "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", + "dev": true, + "requires": { + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "dev": true + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dev": true, + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "parseqs": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", + "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseuri": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", + "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", + "dev": true, + "requires": { + "better-assert": "~1.0.0" + } + }, + "parseurl": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "portfinder": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz", + "integrity": "sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.13.tgz", + "integrity": "sha512-h8SY6kQTd1wISHWjz+E6cswdhMuyBZRb16pSTv3W4zYZ3/YbyWeJdNUeOXB5IdZqE1U76OUEjjjqsC3z2f3hVg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-load-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz", + "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", + "dev": true, + "requires": { + "cosmiconfig": "^4.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "preserve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", + "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dev": true, + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "promise-retry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz", + "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", + "dev": true, + "requires": { + "err-code": "^1.0.0", + "retry": "^0.10.0" + } + }, + "protoduck": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", + "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", + "dev": true, + "requires": { + "genfun": "^5.0.0" + } + }, + "protractor": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.2.tgz", + "integrity": "sha512-zlIj64Cr6IOWP7RwxVeD8O4UskLYPoyIcg0HboWJL9T79F1F0VWtKkGTr/9GN6BKL+/Q/GmM7C9kFVCfDbP5sA==", + "dev": true, + "requires": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.0.6" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + } + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "webdriver-manager": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.1.tgz", + "integrity": "sha512-L9TEQmZs6JbMMRQI1w60mfps265/NCr0toYJl7p/R2OAk6oXAfwI6jqYP7EWae+d7Ad2S2Aj4+rzxoSjqk3ZuA==", + "dev": true, + "requires": { + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + } + } + } + }, + "proxy-addr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", + "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.8.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.1.31", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", + "dev": true + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", + "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", + "dev": true + }, + "randomatic": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", + "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", + "dev": true, + "requires": { + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "randombytes": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", + "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "raw-loader": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz", + "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "optional": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "dependencies": { + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "optional": true + } + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "optional": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "optional": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "optional": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "optional": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", + "dev": true + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regex-cache": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", + "dev": true, + "requires": { + "is-equal-shallow": "^0.1.3" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", + "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", + "dev": true + }, + "rfdc": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", + "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, + "rxjs": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass-graph": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", + "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" + } + }, + "sass-loader": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", + "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "dev": true, + "requires": { + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0", + "semver": "^5.5.0" + } + }, + "saucelabs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", + "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", + "dev": true, + "requires": { + "https-proxy-agent": "^2.2.1" + } + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=", + "dev": true + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "scss-tokenizer": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", + "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", + "dev": true, + "optional": true, + "requires": { + "js-base64": "^2.1.8", + "source-map": "^0.4.2" + }, + "dependencies": { + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selenium-webdriver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", + "dev": true, + "requires": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "dependencies": { + "tmp": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", + "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "dev": true, + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "dev": true + }, + "semver-dsl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz", + "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "semver-intersect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz", + "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==", + "dev": true, + "requires": { + "semver": "^5.0.0" + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + }, + "dependencies": { + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", + "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", + "dev": true + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", + "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "dev": true, + "requires": { + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shelljs": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz", + "integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "smart-buffer": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.0.2.tgz", + "integrity": "sha512-JDhEpTKzXusOqXZ0BUIdH+CjFdO/CR3tLlf5CN34IypI+xMmXW1uB16OOY8z3cICbJlDAVJzNbwBhNO0wt9OAw==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "socket.io": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", + "dev": true, + "requires": { + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", + "socket.io-adapter": "~1.1.0", + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-adapter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", + "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=", + "dev": true + }, + "socket.io-client": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", + "dev": true, + "requires": { + "backo2": "1.0.2", + "base64-arraybuffer": "0.1.5", + "component-bind": "1.0.0", + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "object-component": "0.0.3", + "parseqs": "0.0.5", + "parseuri": "0.0.5", + "socket.io-parser": "~3.2.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "socket.io-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "debug": "~3.1.0", + "isarray": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=", + "dev": true + } + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz", + "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==", + "dev": true, + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "socks": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.2.3.tgz", + "integrity": "sha512-+2r83WaRT3PXYoO/1z+RDEBE7Z2f9YcdQnJ0K/ncXXbV5gJ6wYfNAebYFYiiUjM6E4JyXnPY8cimwyvFYHVUUA==", + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "4.0.2" + } + }, + "socks-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz", + "integrity": "sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw==", + "dev": true, + "requires": { + "agent-base": "~4.2.0", + "socks": "~2.2.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-loader": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz", + "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", + "dev": true, + "requires": { + "async": "^2.5.0", + "loader-utils": "^1.1.0" + }, + "dependencies": { + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + } + } + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz", + "integrity": "sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz", + "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==", + "dev": true + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "speed-measure-webpack-plugin": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.2.5.tgz", + "integrity": "sha512-S/guYjC4Izn5wY2d0+M4zowED/F77Lxh9yjkTZ+XAr244pr9c1MYNcXcRe9lx2hmAj0GPbOrBXgOF2YIp/CZ8A==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", + "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.1" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stats-webpack-plugin": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/stats-webpack-plugin/-/stats-webpack-plugin-0.7.0.tgz", + "integrity": "sha512-NT0YGhwuQ0EOX+uPhhUcI6/+1Sq/pMzNuSCBVT4GbFl/ac6I/JZefBcjlECNfAb1t3GOx5dEj1Z7x0cAxeeVLQ==", + "dev": true, + "requires": { + "lodash": "^4.17.4" + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stdout-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", + "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", + "dev": true, + "optional": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "dev": true + }, + "streamroller": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", + "dev": true, + "requires": { + "date-format": "^1.2.0", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "readable-stream": "^2.3.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "optional": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "optional": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "style-loader": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz", + "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "schema-utils": "^1.0.0" + } + }, + "stylus": { + "version": "0.54.5", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.5.tgz", + "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", + "dev": true, + "requires": { + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" + }, + "dependencies": { + "glob": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.6.tgz", + "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "stylus-loader": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz", + "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "tapable": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz", + "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "optional": true, + "requires": { + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" + } + }, + "terser": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz", + "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==", + "dev": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1", + "source-map-support": "~0.5.9" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.1.tgz", + "integrity": "sha512-GGSt+gbT0oKcMDmPx4SRSfJPE1XaN3kQRWG4ghxKQw9cn5G9x6aCKSsgYdvyM0na9NJ4Drv0RG6jbBByZ5CMjw==", + "dev": true, + "requires": { + "cacache": "^11.0.2", + "find-cache-dir": "^2.0.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "terser": "^3.8.1", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" + }, + "dependencies": { + "cacache": { + "version": "11.3.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", + "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==", + "dev": true, + "requires": { + "bluebird": "^3.5.3", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "find-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz", + "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "yallist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "dev": true + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-array": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", + "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "tree-kill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.0.tgz", + "integrity": "sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "optional": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "true-case-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", + "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.2" + } + }, + "ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "tslint": { + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz", + "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=", + "dev": true, + "requires": { + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.27.2" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typescript": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz", + "integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==", + "dev": true + }, + "uglify-js": { + "version": "3.4.9", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", + "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.17.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "dev": true + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz", + "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==", + "dev": true, + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "useragent": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", + "dev": true, + "requires": { + "lru-cache": "4.1.x", + "tmp": "0.0.x" + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=", + "dev": true + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webdriver-js-extender": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", + "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", + "dev": true, + "requires": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + } + }, + "webpack": { + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.4.tgz", + "integrity": "sha512-NxjD61WsK/a3JIdwWjtIpimmvE6UrRi3yG54/74Hk9rwNj5FPkA4DJCf1z4ByDWLkvZhTZE+P3C/eh6UD5lDcw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/wasm-edit": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", + "acorn": "^5.6.2", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^1.0.0", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.0", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.1.0", + "terser-webpack-plugin": "^1.1.0", + "watchpack": "^1.5.0", + "webpack-sources": "^1.3.0" + }, + "dependencies": { + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-core": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", + "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", + "dev": true, + "requires": { + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" + }, + "dependencies": { + "source-list-map": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", + "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", + "dev": true + }, + "source-map": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "webpack-dev-middleware": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", + "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^2.3.1", + "range-parser": "^1.0.3", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz", + "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.14.tgz", + "integrity": "sha512-mGXDgz5SlTxcF3hUpfC8hrQ11yhAttuUQWf1Wmb+6zo3x6rb7b9mIfuQvAPLdfDRCGRGvakBWHdHOa0I9p/EVQ==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.18.0", + "import-local": "^2.0.0", + "internal-ip": "^3.0.1", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "schema-utils": "^1.0.0", + "selfsigned": "^1.9.1", + "semver": "^5.6.0", + "serve-index": "^1.7.2", + "sockjs": "0.3.19", + "sockjs-client": "1.3.0", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "3.4.0", + "webpack-log": "^2.0.0", + "yargs": "12.0.2" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", + "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", + "dev": true, + "requires": { + "xregexp": "4.0.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "yargs": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-merge": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.1.4.tgz", + "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", + "dev": true, + "requires": { + "lodash": "^4.17.5" + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "webpack-subresource-integrity": { + "version": "1.1.0-rc.6", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.1.0-rc.6.tgz", + "integrity": "sha512-Az7y8xTniNhaA0620AV1KPwWOqawurVVDzQSpPAeR5RwNbL91GoBSJAAo9cfd+GiFHwsS5bbHepBw1e6Hzxy4w==", + "dev": true, + "requires": { + "webpack-core": "^0.6.8" + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "when": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", + "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "worker-farm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", + "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + }, + "dependencies": { + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", + "dev": true + }, + "xmlhttprequest-ssl": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", + "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", + "dev": true + }, + "xregexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true, + "optional": true + } + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "optional": true, + "requires": { + "camelcase": "^3.0.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true, + "optional": true + } + } + }, + "yeast": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", + "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=", + "dev": true + }, + "yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "dev": true + }, + "zone.js": { + "version": "0.8.29", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.29.tgz", + "integrity": "sha512-mla2acNCMkWXBD+c+yeUrBUrzOxYMNFdQ6FGfigGGtEVBPJx07BQeJekjt9DmH1FtZek4E9rE1eRR9qQpxACOQ==" + } + } +} diff --git a/frontend-syscop/package.json b/frontend-syscop/package.json index 6e2121a..0008982 100644 --- a/frontend-syscop/package.json +++ b/frontend-syscop/package.json @@ -1,5 +1,5 @@ { - "name": "frontend-syscop", + "name": "register-front", "version": "0.0.0", "scripts": { "ng": "ng", @@ -11,15 +11,20 @@ }, "private": true, "dependencies": { - "@angular/animations": "~7.2.0", + "@angular/animations": "^7.2.4", + "@angular/cdk": "^7.3.1", "@angular/common": "~7.2.0", "@angular/compiler": "~7.2.0", "@angular/core": "~7.2.0", + "@angular/flex-layout": "^7.0.0-beta.23", "@angular/forms": "~7.2.0", + "@angular/material": "^7.3.1", "@angular/platform-browser": "~7.2.0", "@angular/platform-browser-dynamic": "~7.2.0", "@angular/router": "~7.2.0", "core-js": "^2.5.4", + "d3": "^5.9.1", + "hammerjs": "^2.0.8", "rxjs": "~6.3.3", "tslib": "^1.9.0", "zone.js": "~0.8.26" diff --git a/frontend-syscop/src/app/app-routing.module.ts b/frontend-syscop/src/app/app-routing.module.ts index d425c6f..692ffdb 100644 --- a/frontend-syscop/src/app/app-routing.module.ts +++ b/frontend-syscop/src/app/app-routing.module.ts @@ -1,7 +1,18 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; +import { LoginComponent } from './login/login.component'; +import { SignupComponent } from './signup/signup.component'; +import { InitialViewComponent } from './initial-view/initial-view.component'; +import { HomeComponent } from './home/home.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; -const routes: Routes = []; +const routes: Routes = [ + {path: '', component: InitialViewComponent}, + {path: 'login', component: LoginComponent}, + {path: 'signup', component: SignupComponent}, + {path: 'home', component: HomeComponent}, + {path: 'home/dashboard', component: DashboardComponent} +]; @NgModule({ imports: [RouterModule.forRoot(routes)], diff --git a/frontend-syscop/src/app/app.component.html b/frontend-syscop/src/app/app.component.html index 0f3d9d8..90c6b64 100644 --- a/frontend-syscop/src/app/app.component.html +++ b/frontend-syscop/src/app/app.component.html @@ -1,21 +1 @@ - -
-

- Welcome to {{ title }}! -

- Angular Logo -
-

Here are some links to help you start:

- - - + \ No newline at end of file diff --git a/frontend-syscop/src/app/app.component.spec.ts b/frontend-syscop/src/app/app.component.spec.ts index a4b204a..44b447e 100644 --- a/frontend-syscop/src/app/app.component.spec.ts +++ b/frontend-syscop/src/app/app.component.spec.ts @@ -20,16 +20,16 @@ describe('AppComponent', () => { expect(app).toBeTruthy(); }); - it(`should have as title 'frontend-syscop'`, () => { + it(`should have as title 'register-front'`, () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.debugElement.componentInstance; - expect(app.title).toEqual('frontend-syscop'); + expect(app.title).toEqual('register-front'); }); it('should render title in a h1 tag', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.debugElement.nativeElement; - expect(compiled.querySelector('h1').textContent).toContain('Welcome to frontend-syscop!'); + expect(compiled.querySelector('h1').textContent).toContain('Welcome to register-front!'); }); }); diff --git a/frontend-syscop/src/app/app.component.ts b/frontend-syscop/src/app/app.component.ts index 08c236f..c24ffb2 100644 --- a/frontend-syscop/src/app/app.component.ts +++ b/frontend-syscop/src/app/app.component.ts @@ -6,5 +6,5 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.css'] }) export class AppComponent { - title = 'frontend-syscop'; + title = 'register-front'; } diff --git a/frontend-syscop/src/app/app.module.ts b/frontend-syscop/src/app/app.module.ts index 2c3ba29..16b2164 100644 --- a/frontend-syscop/src/app/app.module.ts +++ b/frontend-syscop/src/app/app.module.ts @@ -1,16 +1,59 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { MatTabsModule, MatSidenavModule, MatToolbarModule, MatIconModule, + MatButtonModule, MatListModule, MatNativeDateModule } from '@angular/material'; import { AppRoutingModule } from './app-routing.module'; -import { AppComponent } from './app.component'; +import { AppComponent, } from './app.component'; +import { LoginComponent } from './login/login.component'; +import { SignupComponent } from './signup/signup.component'; +import { InitialViewComponent } from './initial-view/initial-view.component'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { LayoutModule } from '@angular/cdk/layout'; +import { HomeComponent } from './home/home.component'; +import { MatFormFieldModule, MatError } from '@angular/material/form-field'; +import { MatCardModule} from '@angular/material/card'; +import { FormsModule, ReactiveFormsModule} from '@angular/forms'; +import { MatInputModule} from '@angular/material/input'; +import { MatDatepickerModule} from '@angular/material/datepicker'; +import { HttpClientModule } from '@angular/common/http'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { MatBadgeModule } from '@angular/material/badge'; +import { DashboardComponent } from './dashboard/dashboard.component'; + + @NgModule({ declarations: [ - AppComponent + AppComponent, + LoginComponent, + SignupComponent, + InitialViewComponent, + HomeComponent, + DashboardComponent, ], imports: [ BrowserModule, - AppRoutingModule + AppRoutingModule, + HttpClientModule, + BrowserAnimationsModule, + FormsModule, + ReactiveFormsModule, + MatTabsModule, + MatSidenavModule, + MatToolbarModule, + MatIconModule, + MatButtonModule, + LayoutModule, + MatListModule, + MatFormFieldModule, + MatCardModule, + MatInputModule, + MatDatepickerModule, + MatNativeDateModule, + FlexLayoutModule, + MatBadgeModule + ], providers: [], bootstrap: [AppComponent] diff --git a/frontend-syscop/src/app/auth/auth-interceptor.ts b/frontend-syscop/src/app/auth/auth-interceptor.ts new file mode 100644 index 0000000..8a62866 --- /dev/null +++ b/frontend-syscop/src/app/auth/auth-interceptor.ts @@ -0,0 +1,26 @@ +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; + +import { TokenStorageService } from './token-storage.service'; + +const TOKEN_HEADER_KEY = 'Authorization'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + + constructor(private token: TokenStorageService) { } + + intercept(req: HttpRequest, next: HttpHandler) { + let authReq = req; + const token = this.token.getToken(); + if (token != null) { + authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) }); + } + return next.handle(authReq); + } +} + +export const httpInterceptorProviders = [ + { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } +]; diff --git a/frontend-syscop/src/app/auth/auth.service.spec.ts b/frontend-syscop/src/app/auth/auth.service.spec.ts new file mode 100644 index 0000000..bd98634 --- /dev/null +++ b/frontend-syscop/src/app/auth/auth.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AuthService] + }); + }); + + it('should be created', inject([AuthService], (service: AuthService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/frontend-syscop/src/app/auth/auth.service.ts b/frontend-syscop/src/app/auth/auth.service.ts new file mode 100644 index 0000000..525d202 --- /dev/null +++ b/frontend-syscop/src/app/auth/auth.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { JwtResponse } from './jwt-response'; +import { AuthLoginInfo } from './login-info'; +import { SignUpInfo } from './signup-info'; + +const httpOptions = { + headers: new HttpHeaders({ 'Content-Type': 'application/json' }) +}; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + + // private loginUrl = 'http://13.232.165.99:8090/api/auth/signin'; + // private signupUrl = 'http://13.232.165.99:8090/api/auth/signup'; + + public loginUrl = 'http://172.23.239.148:8090/api/auth/signin'; + public signupUrl = 'http://172.23.239.148:8090/api/auth/signup'; + + constructor(private http: HttpClient) { + } + + attemptAuth(credentials: AuthLoginInfo): Observable { + return this.http.post(this.loginUrl, credentials, httpOptions); + } + + signUp(info: SignUpInfo): Observable { + return this.http.post(this.signupUrl, info, httpOptions); + } +} diff --git a/frontend-syscop/src/app/auth/jwt-response.ts b/frontend-syscop/src/app/auth/jwt-response.ts new file mode 100644 index 0000000..220f1d3 --- /dev/null +++ b/frontend-syscop/src/app/auth/jwt-response.ts @@ -0,0 +1,6 @@ +export class JwtResponse { + accessToken: string; + type: string; + username: string; + authorities: string[]; +} diff --git a/frontend-syscop/src/app/auth/login-info.ts b/frontend-syscop/src/app/auth/login-info.ts new file mode 100644 index 0000000..9658c8f --- /dev/null +++ b/frontend-syscop/src/app/auth/login-info.ts @@ -0,0 +1,9 @@ +export class AuthLoginInfo { + username: string; + password: string; + + constructor(username: string, password: string) { + this.username = username; + this.password = password; + } +} diff --git a/frontend-syscop/src/app/auth/signup-info.ts b/frontend-syscop/src/app/auth/signup-info.ts new file mode 100644 index 0000000..30b9c16 --- /dev/null +++ b/frontend-syscop/src/app/auth/signup-info.ts @@ -0,0 +1,15 @@ +export class SignUpInfo { + name: string; + username: string; + email: string; + role: string[]; + password: string; + + constructor(name: string, username: string, email: string, password: string) { + this.name = name; + this.username = username; + this.email = email; + this.password = password; + this.role = ['user']; + } +} diff --git a/frontend-syscop/src/app/auth/token-storage.service.spec.ts b/frontend-syscop/src/app/auth/token-storage.service.spec.ts new file mode 100644 index 0000000..dddaf30 --- /dev/null +++ b/frontend-syscop/src/app/auth/token-storage.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { TokenStorageService } from './token-storage.service'; + +describe('TokenStorageService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [TokenStorageService] + }); + }); + + it('should be created', inject([TokenStorageService], (service: TokenStorageService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/frontend-syscop/src/app/auth/token-storage.service.ts b/frontend-syscop/src/app/auth/token-storage.service.ts new file mode 100644 index 0000000..9589a76 --- /dev/null +++ b/frontend-syscop/src/app/auth/token-storage.service.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; + +const TOKEN_KEY = 'AuthToken'; +const USERNAME_KEY = 'AuthUsername'; +const AUTHORITIES_KEY = 'AuthAuthorities'; + +@Injectable({ + providedIn: 'root' +}) +export class TokenStorageService { + private roles: Array = []; + constructor() { } + + signOut() { + window.sessionStorage.clear(); + } + + public saveToken(token: string) { + window.sessionStorage.removeItem(TOKEN_KEY); + window.sessionStorage.setItem(TOKEN_KEY, token); + } + + public getToken(): string { + return sessionStorage.getItem(TOKEN_KEY); + } + + public saveUsername(username: string) { + window.sessionStorage.removeItem(USERNAME_KEY); + window.sessionStorage.setItem(USERNAME_KEY, username); + } + + public getUsername(): string { + return sessionStorage.getItem(USERNAME_KEY); + } + + public saveAuthorities(authorities: string[]) { + window.sessionStorage.removeItem(AUTHORITIES_KEY); + window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities)); + } + + public getAuthorities(): string[] { + this.roles = []; + + if (sessionStorage.getItem(TOKEN_KEY)) { + JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => { + this.roles.push(authority.authority); + }); + } + + return this.roles; + } +} diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.css b/frontend-syscop/src/app/dashboard/dashboard.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.html b/frontend-syscop/src/app/dashboard/dashboard.component.html new file mode 100644 index 0000000..fa60057 --- /dev/null +++ b/frontend-syscop/src/app/dashboard/dashboard.component.html @@ -0,0 +1,3 @@ + + Click anywhere + \ No newline at end of file diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts b/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts new file mode 100644 index 0000000..9c996c3 --- /dev/null +++ b/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DashboardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.ts b/frontend-syscop/src/app/dashboard/dashboard.component.ts new file mode 100644 index 0000000..5653abc --- /dev/null +++ b/frontend-syscop/src/app/dashboard/dashboard.component.ts @@ -0,0 +1,33 @@ +import { Component, AfterContentInit } from '@angular/core'; +import * as d3 from 'd3'; +@Component({ + selector: 'app-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.css'] +}) +export class DashboardComponent implements AfterContentInit { + + title = 'app'; + + + radius = 10; + + ngAfterContentInit() { + d3.select('p').style('color', 'red'); + } + + colorMe() { + d3.select('button').style('color', 'red'); + } + + clicked(event: any) { + d3.select(event.target).append('circle') + .attr('cx', event.x) + .attr('cy', event.y) + .attr('r', () => { + return this.radius; + }) + .attr('fill', 'red'); + } + +} diff --git a/frontend-syscop/src/app/home/home.component.css b/frontend-syscop/src/app/home/home.component.css new file mode 100644 index 0000000..a765961 --- /dev/null +++ b/frontend-syscop/src/app/home/home.component.css @@ -0,0 +1,30 @@ +.sidenav-container { + height: 100%; +} + +.sidenav { + width: 200px; +} + +.sidenav .mat-toolbar { + background: rgba(20,30,45,1); + color: white; +} + +.mat-toolbar.mat-primary { + position: sticky; + top: 0; + z-index: 1000; + background: rgba(20,30,45,1); +} +.sidenav{ + background: rgba(20,30,45,0.5); + color: white; +} +.example-icon { + padding: 0 14px; +} + +.example-spacer { + flex: 1 1 auto; +} diff --git a/frontend-syscop/src/app/home/home.component.html b/frontend-syscop/src/app/home/home.component.html new file mode 100644 index 0000000..4a8f695 --- /dev/null +++ b/frontend-syscop/src/app/home/home.component.html @@ -0,0 +1,46 @@ + + + SYSCOP + + + dashboard + Dashboard + + + notification_important + Notifications + + + trending_up + Data Sources + + + account_circle + Admin + + + work + Company + + + + + + + + + SYSCOP + + dashboard + notification_important + account_circle + + + + + diff --git a/frontend-syscop/src/app/home/home.component.spec.ts b/frontend-syscop/src/app/home/home.component.spec.ts new file mode 100644 index 0000000..376ddab --- /dev/null +++ b/frontend-syscop/src/app/home/home.component.spec.ts @@ -0,0 +1,42 @@ +import { LayoutModule } from '@angular/cdk/layout'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { + MatButtonModule, + MatIconModule, + MatListModule, + MatSidenavModule, + MatToolbarModule, +} from '@angular/material'; + +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [HomeComponent], + imports: [ + NoopAnimationsModule, + LayoutModule, + MatButtonModule, + MatIconModule, + MatListModule, + MatSidenavModule, + MatToolbarModule, + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should compile', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/home/home.component.ts b/frontend-syscop/src/app/home/home.component.ts new file mode 100644 index 0000000..c6cb1b9 --- /dev/null +++ b/frontend-syscop/src/app/home/home.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; +import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { AppRoutingModule } from '../app-routing.module'; + +@Component({ + selector: 'app-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.css'] +}) +export class HomeComponent { + + isHandset$: Observable = this.breakpointObserver.observe(Breakpoints.Handset) + .pipe( + map(result => result.matches) + ); + + constructor(private breakpointObserver: BreakpointObserver) { + } + +} diff --git a/frontend-syscop/src/app/initial-view/initial-view.component.css b/frontend-syscop/src/app/initial-view/initial-view.component.css new file mode 100644 index 0000000..caacbf0 --- /dev/null +++ b/frontend-syscop/src/app/initial-view/initial-view.component.css @@ -0,0 +1,39 @@ +/* Split the screen in half */ +.split { + height: 100%; + width: 50%; + position: fixed; + z-index: 1; + top: 0; + overflow-x: hidden; + padding-top: 20px; + } + + /* Control the left side */ + .left { + left: 0; + background-color: deepskyblue; + } + + /* Control the right side */ + .right { + right: 0; + background-color: white; + } + + /* If you want the content centered horizontally and vertically */ + .centered { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; + } + .log,.sign{ + background: deepskyblue; + color:white; + width: 25rem; + height:2rem; + border-radius: 5rem; + + } \ No newline at end of file diff --git a/frontend-syscop/src/app/initial-view/initial-view.component.html b/frontend-syscop/src/app/initial-view/initial-view.component.html new file mode 100644 index 0000000..6300e63 --- /dev/null +++ b/frontend-syscop/src/app/initial-view/initial-view.component.html @@ -0,0 +1,20 @@ +
+
+

From Metrics to Insights

+

Power your metrics and alerting with a leading open-source solution.

+
+
+ +
+
+

Join Syscop today.

+ + + + + + + +
+
+
\ No newline at end of file diff --git a/frontend-syscop/src/app/initial-view/initial-view.component.spec.ts b/frontend-syscop/src/app/initial-view/initial-view.component.spec.ts new file mode 100644 index 0000000..70a3884 --- /dev/null +++ b/frontend-syscop/src/app/initial-view/initial-view.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { InitialViewComponent } from './initial-view.component'; + +describe('InitialViewComponent', () => { + let component: InitialViewComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ InitialViewComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(InitialViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/initial-view/initial-view.component.ts b/frontend-syscop/src/app/initial-view/initial-view.component.ts new file mode 100644 index 0000000..0e62292 --- /dev/null +++ b/frontend-syscop/src/app/initial-view/initial-view.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-initial-view', + templateUrl: './initial-view.component.html', + styleUrls: ['./initial-view.component.css'] +}) +export class InitialViewComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/frontend-syscop/src/app/login/login.component.css b/frontend-syscop/src/app/login/login.component.css new file mode 100644 index 0000000..e587610 --- /dev/null +++ b/frontend-syscop/src/app/login/login.component.css @@ -0,0 +1,26 @@ +.example-card { + align-items: center; + position: absolute; + width: 30%; + left :35%; + top:20%; +} +@media only screen and (max-width: 700px) { + .example-card { + align-items: center; + position: absolute; + width: 50%; + left :25%; + top:20%; + + } +} +@media only screen and (max-width: 400px) { + .example-card { + align-items: center; + position: absolute; + width: 70%; + left :15%; + top:20%; + } +} \ No newline at end of file diff --git a/frontend-syscop/src/app/login/login.component.html b/frontend-syscop/src/app/login/login.component.html new file mode 100644 index 0000000..59c1791 --- /dev/null +++ b/frontend-syscop/src/app/login/login.component.html @@ -0,0 +1,72 @@ + + + Login + + +
+ + + + + + + +
+ + Enter your Username + + sentiment_very_satisfied + Username must be unique + +
+ + Enter your Password + + {{hide ? 'visibility_off' : 'visibility'}} + +
+
+
+ + + +
+ + \ No newline at end of file diff --git a/frontend-syscop/src/app/login/login.component.spec.ts b/frontend-syscop/src/app/login/login.component.spec.ts new file mode 100644 index 0000000..d6d85a8 --- /dev/null +++ b/frontend-syscop/src/app/login/login.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let component: LoginComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ LoginComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/login/login.component.ts b/frontend-syscop/src/app/login/login.component.ts new file mode 100644 index 0000000..46c6e1f --- /dev/null +++ b/frontend-syscop/src/app/login/login.component.ts @@ -0,0 +1,96 @@ +// import { Component, OnInit } from '@angular/core'; +// import { FormGroup, FormControl, Validators } from '@angular/forms'; +// import { UserService } from '../user.service'; + +// @Component({ +// selector: 'app-login', +// templateUrl: './login.component.html', +// styleUrls: ['./login.component.css'] +// }) +// export class LoginComponent implements OnInit { + +// myForm: FormGroup; +// constructor(private userService: UserService) { } + +// ngOnInit() { +// this.myForm = new FormGroup({ +// username: new FormControl('', [Validators.required ]), +// password: new FormControl('', [Validators.required ]), +// }); +// } +// onSubmit() { +// // stop print if form is invalid +// if (this.myForm.invalid) { +// console.log('register form is invalid '); +// return; +// } +// console.log('registerForm.value : ', this.myForm.value); +// this.userService.saveUser(this.myForm.value); +// } + +// } +import { Component, OnInit } from '@angular/core'; + +import { AuthService } from '../auth/auth.service'; +import { TokenStorageService } from '../auth/token-storage.service'; +import { AuthLoginInfo } from '../auth/login-info'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-login', + templateUrl: './login.component.html', + styleUrls: ['./login.component.css'] +}) +export class LoginComponent implements OnInit { + form: any = {}; + isLoggedIn = false; + isLoginFailed = false; + errorMessage = ''; + roles: string[] = []; + private loginInfo: AuthLoginInfo; + + constructor(private authService: AuthService, private tokenStorage: TokenStorageService, private router: Router) { } + + ngOnInit() { + if (this.tokenStorage.getToken()) { + this.isLoggedIn = true; + this.roles = this.tokenStorage.getAuthorities(); + } + this.form = new FormGroup({ + username: new FormControl('', [Validators.required ]), + password: new FormControl('', [Validators.required ]), + }); + } + + onSubmit() { + if (this.form.invalid) { + console.log('form is invalid '); + return; + } + console.log('form.value : ', this.form.value); + console.log(this.form); + + this.loginInfo = new AuthLoginInfo( + this.form.controls['username'].value, + this.form.controls['password'].value); + + this.authService.attemptAuth(this.loginInfo).subscribe( + data => { + this.tokenStorage.saveToken(data.accessToken); + this.tokenStorage.saveUsername(data.username); + this.tokenStorage.saveAuthorities(data.authorities); + + this.isLoginFailed = false; + this.isLoggedIn = true; + this.roles = this.tokenStorage.getAuthorities(); + this.router.navigateByUrl('/home'); + }, + error => { + console.log(error); + this.errorMessage = error.error.message; + this.isLoginFailed = true; + } + ); + } +} diff --git a/frontend-syscop/src/app/signup/signup.component.css b/frontend-syscop/src/app/signup/signup.component.css new file mode 100644 index 0000000..3058045 --- /dev/null +++ b/frontend-syscop/src/app/signup/signup.component.css @@ -0,0 +1,19 @@ +.example-card { + align-items: center; + width: 30%; + left :35%; +} +@media only screen and (max-width: 700px) { + .example-card { + align-items: center; + width: 50%; + left :25%; + } +} +@media only screen and (max-width: 400px) { + .example-card { + align-items: center; + width: 70%; + left :15%; + } +} \ No newline at end of file diff --git a/frontend-syscop/src/app/signup/signup.component.html b/frontend-syscop/src/app/signup/signup.component.html new file mode 100644 index 0000000..df3c991 --- /dev/null +++ b/frontend-syscop/src/app/signup/signup.component.html @@ -0,0 +1,76 @@ + + + Signup + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + Enter your Username + + sentiment_very_satisfied + Username must be unique + +
+ + Enter your password + + {{hide ? 'visibility_off' : 'visibility'}} + +
+ + Enter your Phone no. + + phone + +
+ + Enter your Email + + email + +
+ + Enter your Company + + work + +
+ + Enter your DOB + + + + +
+ +
+
+
+
\ No newline at end of file diff --git a/frontend-syscop/src/app/signup/signup.component.spec.ts b/frontend-syscop/src/app/signup/signup.component.spec.ts new file mode 100644 index 0000000..43e46a5 --- /dev/null +++ b/frontend-syscop/src/app/signup/signup.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SignupComponent } from './signup.component'; + +describe('SignupComponent', () => { + let component: SignupComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SignupComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SignupComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/signup/signup.component.ts b/frontend-syscop/src/app/signup/signup.component.ts new file mode 100644 index 0000000..30f5ca3 --- /dev/null +++ b/frontend-syscop/src/app/signup/signup.component.ts @@ -0,0 +1,34 @@ +import { Component, OnInit } from '@angular/core'; +import { UserService } from '../user.service'; +import { FormControl, Validators, FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-signup', + templateUrl: './signup.component.html', + styleUrls: ['./signup.component.css'] +}) +export class SignupComponent implements OnInit { + myForm: FormGroup; + constructor(private userService: UserService) { } + + ngOnInit() { + this.myForm = new FormGroup({ + username: new FormControl('', [Validators.required, Validators.minLength(8), Validators.maxLength(20) ]), + password: new FormControl('', [Validators.required ]), + phoneNumber: new FormControl('', [Validators.required, Validators.pattern('[0-9]{10}')]), + email: new FormControl('', [Validators.required, Validators.email]), + company: new FormControl('', [Validators.required ]), + dob: new FormControl('', [Validators.required ]), + }); + } + onSubmit() { + // stop print if form is invalid + if (this.myForm.invalid) { + console.log('register form is invalid '); + return; + } + console.log('registerForm.value : ', this.myForm.value); + this.userService.saveUser(this.myForm.value); + } + +} diff --git a/frontend-syscop/src/app/user.service.spec.ts b/frontend-syscop/src/app/user.service.spec.ts new file mode 100644 index 0000000..9e7fd1c --- /dev/null +++ b/frontend-syscop/src/app/user.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { UserService } from './user.service'; + +describe('UserService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: UserService = TestBed.get(UserService); + expect(service).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/user.service.ts b/frontend-syscop/src/app/user.service.ts new file mode 100644 index 0000000..e46d3e7 --- /dev/null +++ b/frontend-syscop/src/app/user.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class UserService { + + public url = 'http://172.23.239.170:8096/api/v1/register'; + public userUrl = 'http://172.23.239.148:8090/api/test/user'; + + constructor(private http: HttpClient) { } + saveUser(user: any) { + this.http.post(this.url, user, {responseType: 'text'} ).subscribe((data) => { + console.log(data); + }); + + console.log(user); + } + getUserBoard(): Observable { + return this.http.get(this.userUrl, { responseType: 'text' }); + } + // getUsers(): Observable { + // return this + // .http + // .get(`${this.url}/users`); + // } + // deleteuser(id) { + // return this.http.delete(`${this.url}/user/${id}`, {responseType: 'text'}); + + // } + // updateUser(): Observable { + // return this.http.put(`${this.url}/user`, User, {responseType: 'text'}) ; + // } + +} diff --git a/frontend-syscop/src/index.html b/frontend-syscop/src/index.html index 2455033..c4c9440 100644 --- a/frontend-syscop/src/index.html +++ b/frontend-syscop/src/index.html @@ -2,11 +2,12 @@ - FrontendSyscop + RegisterFront + diff --git a/frontend-syscop/src/styles.css b/frontend-syscop/src/styles.css index 90d4ee0..341daf1 100644 --- a/frontend-syscop/src/styles.css +++ b/frontend-syscop/src/styles.css @@ -1 +1,5 @@ /* You can add global styles to this file, and also import other style files */ +@import "~@angular/material/prebuilt-themes/indigo-pink.css"; +body{ + margin: 0; +} From f1bb5d7d848272d51943a344f1b0c263ed1a34ed Mon Sep 17 00:00:00 2001 From: sushantanshu Date: Tue, 12 Feb 2019 14:39:48 +0530 Subject: [PATCH 06/38] front-end --- eventbrite-service/.gitignore | 25 --- eventbrite-service/Dockerfile | 6 - eventbrite-service/README.md | 0 eventbrite-service/pom.xml | 127 --------------- .../nextevent/NexteventApplication.java | 16 -- .../nextevent/config/SwaggerConfig.java | 22 --- .../nextevent/controller/EventController.java | 96 ------------ .../controller/GlobalExceptionHandler.java | 55 ------- .../exception/EventAlreadyExist.java | 9 -- .../nextevent/exception/EventNotFound.java | 11 -- .../stackroute/nextevent/model/Category.java | 29 ---- .../com/stackroute/nextevent/model/Event.java | 63 -------- .../nextevent/model/SubCategory.java | 17 -- .../com/stackroute/nextevent/model/Venu.java | 20 --- .../nextevent/repository/EventRepository.java | 13 -- .../nextevent/service/EventService.java | 17 -- .../nextevent/service/EventServiceImpl.java | 145 ------------------ .../src/main/resources/application.properties | 27 ---- .../nextevent/NexteventApplicationTests.java | 17 -- .../controller/EventControllerTest.java | 105 ------------- .../repository/EventRepositoryTestIt.java | 37 ----- 21 files changed, 857 deletions(-) delete mode 100644 eventbrite-service/.gitignore delete mode 100644 eventbrite-service/Dockerfile delete mode 100644 eventbrite-service/README.md delete mode 100644 eventbrite-service/pom.xml delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java delete mode 100644 eventbrite-service/src/main/resources/application.properties delete mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java delete mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java delete mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java diff --git a/eventbrite-service/.gitignore b/eventbrite-service/.gitignore deleted file mode 100644 index 82eca33..0000000 --- a/eventbrite-service/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/build/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ \ No newline at end of file diff --git a/eventbrite-service/Dockerfile b/eventbrite-service/Dockerfile deleted file mode 100644 index 9ead3ad..0000000 --- a/eventbrite-service/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM openjdk:11 - -ADD ./target/nextevent-0.0.1-SNAPSHOT.jar /event/src/nextevent-0.0.1-SNAPSHOT.jar -EXPOSE 8082 -WORKDIR event/src -ENTRYPOINT ["java","-jar","nextevent-0.0.1-SNAPSHOT.jar"] diff --git a/eventbrite-service/README.md b/eventbrite-service/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/eventbrite-service/pom.xml b/eventbrite-service/pom.xml deleted file mode 100644 index c4816bc..0000000 --- a/eventbrite-service/pom.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - 4.0.0 - - com.stackroute - syscop - 0.0.1-SNAPSHOT - ../pom.xml - - com.stackroute - eventbrite-service - 0.0.1-SNAPSHOT - eventbrite-service - nextEvent project in Spring Boot - - - UTF-8 - UTF-8 - 1.8 - ${java.version} - ${java.version} - - - - - - - - - - - - org.springframework.boot - spring-boot-starter-data-mongodb - - - io.springfox - springfox-swagger2 - 2.9.2 - - - org.springframework.cloud - spring-cloud-starter-netflix-zuul - - - io.springfox - springfox-swagger-ui - 2.9.2 - - - - org.springframework.cloud - spring-cloud-starter - - - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - - - org.springframework.boot - spring-boot-starter-web - - - - - - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.projectlombok - lombok-maven - 1.18.4.0 - pom - - - - org.projectlombok - lombok - 1.18.4 - provided - - - org.springframework.cloud - spring-cloud-commons - 2.0.1.RELEASE - - - - - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - - - - - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java deleted file mode 100644 index 36db246..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.stackroute.nextevent; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; - -@SpringBootApplication -@EnableEurekaClient -public class NexteventApplication { - - public static void main(String[] args) { - SpringApplication.run(NexteventApplication.class, args); - } - -} - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java deleted file mode 100644 index 4eeb351..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.stackroute.nextevent.config; - - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -@Configuration -@EnableSwagger2 -public class SwaggerConfig { - @Bean - public Docket customImplementation(){ - return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.basePackage("com.stackroute.nextevent")) - .build(); - } - } - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java deleted file mode 100644 index da9b5b2..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.stackroute.nextevent.controller; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import com.stackroute.nextevent.model.Event; -import com.stackroute.nextevent.service.EventService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("api/v1") -public class EventController { - private static Logger log = LoggerFactory.getLogger(EventController.class); - - private ResponseEntity responseEntity; - private EventService eventService; - - @Autowired - private EventController(final EventService eventService) { - this.eventService = eventService; - } - - @GetMapping("events") - public ResponseEntity getAllEvent() throws EventNotFound { - log.info("getAllEvevnt hit"); - responseEntity = new ResponseEntity(eventService.getAllEvent(), HttpStatus.OK); - return responseEntity; - } - - @PostMapping("events") - public ResponseEntity saveEvent(@RequestBody Event event) throws EventAlreadyExist{ - eventService.saveEvent(event); - responseEntity = new ResponseEntity(event, HttpStatus.CREATED); - - return responseEntity; - } - - @DeleteMapping("events/{id}") - public ResponseEntity deleteEvent(@PathVariable("id") String id) throws EventNotFound { - eventService.deleteEvent(id); - log.info("event mapping hit event deleted======"); - responseEntity = new ResponseEntity("Event deleted successfully", HttpStatus.OK); - return responseEntity; - } - - - @PutMapping("events/{id}") - public ResponseEntity updateEvent(@PathVariable("id") String id,@RequestBody Event event) throws EventNotFound { - log.info("upadte event hit"); - Event event1= eventService.updateEvent(event,id); - log.info("updated event+++++"+event1); - return responseEntity; - } - - @GetMapping("event/{id}") - public ResponseEntity getEventById(@PathVariable("id") String id) throws EventNotFound { - log.info("getEvent hit by Id"); - responseEntity = new ResponseEntity(eventService.getEventById(id), HttpStatus.OK); - return responseEntity; - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java deleted file mode 100644 index fcf5f58..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.stackroute.nextevent.controller; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; - - -@ControllerAdvice -//@Log4j -public class GlobalExceptionHandler { - private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); - @ResponseStatus(value = HttpStatus.CONFLICT,reason = "Event Already Exists") - @ExceptionHandler(EventAlreadyExist.class) - public void handleEventAlreadyExists(EventAlreadyExist e) - { - log.error("Event alredy Exists"); - } - - @ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "Event Not Found") - @ExceptionHandler(EventNotFound.class) - public void handleEventNotFound() - { - log.error("Event Not Found"); - } - - @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "Something went wrong ") - public void handleException(Exception e) - { - log.error("Try!!! After sometime"); - } -// @ResponseStatus(value= HttpStatus.BAD_REQUEST, reason="Not all mandatory fields are filled") -// @ExceptionHandler(EmptyFieldException.class) -// public void handleEmptyFieldException(EmptyFieldException e){ -// log.error("Not all mandatory fields are filled", e); -// } -// -// @ResponseStatus(value= HttpStatus.CONFLICT, reason="User already exists") -// @ExceptionHandler(UserAlreadyExistsException.class) -// public void handleUserAlreadyExistsException(UserAlreadyExistsException e){ -// log.error("User already exists", e); -// } -// -// @ResponseStatus(value= HttpStatus.NOT_FOUND, reason="User does not exist") -// @ExceptionHandler(UserDoesNotExistException.class) -// public void handleUserDoesNotExistException(UserDoesNotExistException e){ -// log.error("User does not exist", e); -// } - - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java deleted file mode 100644 index ca55e5c..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.stackroute.nextevent.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.CONFLICT, reason = "Event already exists on server") -public class EventAlreadyExist extends Exception { - public EventAlreadyExist(String message){super(message);} -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java deleted file mode 100644 index 34369e4..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.stackroute.nextevent.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Specified Event Not Found on server") -public class EventNotFound extends Exception { - - public EventNotFound(String message){super(message);} - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java deleted file mode 100644 index d2e3706..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.stackroute.nextevent.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Category { - private String categoryId; - private String name; - private List subCategory; - private int ageRestriction; - - @Override - public String toString() { - return "Category{" + - "categoryId='" + categoryId + '\'' + - ", name='" + name + '\'' + - ", subCategory=" + subCategory + - ", ageRestriction=" + ageRestriction + - '}'; - } -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java deleted file mode 100644 index 778a43d..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.stackroute.nextevent.model; - -import javax.validation.constraints.NotNull; -import java.util.Date; - - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -@Document -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Event { - - @Id - private String id; - - @NotNull - private String eventId; - - @NotNull - private String name; - - @NotNull - private String description; - - private Date created; - - //@NotNull - private Date start; - - //@NotNull - private Date end; - - private Venu venu; - - private Category category; - - @NotNull - private String imageUrl; - - @Override - public String toString() { - return "Event{" + - "id='" + id + '\'' + - ", eventId='" + eventId + '\'' + - ", name='" + name + '\'' + - ", description='" + description + '\'' + - ", created=" + created + - ", start=" + start + - ", end=" + end + - ", venu=" + venu + - ", category=" + category + - ", imageUrl='" + imageUrl + '\'' + - '}'; - } -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java deleted file mode 100644 index 59bca65..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.stackroute.nextevent.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class SubCategory { - String subcategoryId; - String name; - - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java deleted file mode 100644 index aa05a20..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.stackroute.nextevent.model; - - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Venu { - String venuId; - String address; - String name; - int capacity; - - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java deleted file mode 100644 index 74e6132..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.stackroute.nextevent.repository; - -import com.stackroute.nextevent.model.Event; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface EventRepository extends MongoRepository { - - Event findByEventId(String id); - -} - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java deleted file mode 100644 index 717f8db..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.stackroute.nextevent.service; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import com.stackroute.nextevent.model.Event; - -import java.util.List; - -public interface EventService { - - Event saveEvent(Event event) throws EventAlreadyExist; - boolean deleteEvent(String id) throws EventNotFound; - Event updateEvent(Event event,String id) throws EventNotFound; - Event getEventById(String id) throws EventNotFound; - List getAllEvent() throws EventNotFound; - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java deleted file mode 100644 index ff536b8..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.stackroute.nextevent.service; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import com.stackroute.nextevent.model.Event; -import com.stackroute.nextevent.repository.EventRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Optional; - -@Service -public class EventServiceImpl implements EventService { - - private static Logger log = LoggerFactory.getLogger(EventServiceImpl.class); - private EventRepository eventRepository; - // private Event event; - - @Autowired - EventServiceImpl(EventRepository eventRepository) - { - this.eventRepository = eventRepository; - } - - @Override - public Event saveEvent(Event event) throws EventAlreadyExist { - //return null; - Optional optional = eventRepository.findById(event.getId()); - if(optional.isPresent()) - throw new EventAlreadyExist("Event already exists"); - else - return eventRepository.save(event); - - } -// log.info("we are in SaveEvent"); -// System.out.println("++++++++++++++++++++++++++++"+eventRepository.findById(event.getId())); -// Optional optional = eventRepository.findById(event.getId()); -// if((optional.isPresent())){ -// throw new EventAlreadyExist("Event already exists"); -// } -// else { -// log.info("Event is+++++++++++" + event); -// return eventRepository.save(event); -// } - // } - - @Override - public boolean deleteEvent(String id) throws EventNotFound { - //return false; - // boolean status = false; - Optional optional = eventRepository.findById(id); - log.info("check event+++++++"+optional); - if(optional.isPresent()) { - eventRepository.deleteById(id); - System.out.println("Id is"+id); - // eventRepository.deleteById(id); - return true; - } - else{ - throw new EventNotFound("Event not Found"); - } - - //return status; -// Event event = eventRepository.getOne(id); -// if(event!=null) -// { -// eventRepository.delete(event); -// return true; -// } -// else { -// throw new EventNotFound("Event not found with this id"); -// } - - } - - @Override - public Event updateEvent(Event event, String id) throws EventNotFound { - //return null; - //return null; - Optional optional = eventRepository.findById(id); - if(optional.isPresent()){ - event = eventRepository.findById(id).get(); - event.setCreated(event.getCreated()); - event.setDescription(event.getDescription()); - event.setEnd(event.getEnd()); - event.setStart(event.getStart()); - event.setImageUrl(event.getImageUrl()); - event.setName(event.getName()); - event.setCategory(event.getCategory()); - log.info("event is======"+event); -// System.out.println(event); -// System.out.println("updated evet+++++++++++"+event); - log.info("updated event Saved"); - eventRepository.save(event); - // System.out.println("updated ++++++"+eventRepository.save(event)); - // muzixRepository.save(track); - //return updatedEvent; - - } - else - { - throw new EventNotFound("Event not found"); - } - return event; -// Event eventToBeUpdated = eventRepository.getOne(event.getId()); -// if(event!=null) -// { -// eventToBeUpdated.setCreated(event.getCreated()); -// eventToBeUpdated.setDescription(event.getDescription()); -// eventToBeUpdated.setEnd(event.getEnd()); -// eventToBeUpdated.setStart(event.getStart()); -// eventToBeUpdated.setImageUrl(event.getImageUrl()); -// eventToBeUpdated.setName(event.getName()); -// return eventRepository.save(eventToBeUpdated); -// -// } -// else { -// throw new EventNotFound("Event not found with this id"); -// } - } - - @Override - public Event getEventById(String id) throws EventNotFound { - //return null; - Optional optional = eventRepository.findById(id); - if(optional.isPresent()) { - log.info("inside if evntId found"); - return eventRepository.findById(id).get(); - } - else{ - throw new EventNotFound("Event not Found"); - } - - } - - @Override - public List getAllEvent() throws EventNotFound { - //return null; - return eventRepository.findAll(); - - } -} diff --git a/eventbrite-service/src/main/resources/application.properties b/eventbrite-service/src/main/resources/application.properties deleted file mode 100644 index 68822ed..0000000 --- a/eventbrite-service/src/main/resources/application.properties +++ /dev/null @@ -1,27 +0,0 @@ - -#spring.datasource.url=jdbc:mysql://localhost:3306/eventdata -#spring.datasource.username=root -#spring.datasource.password=123 -#spring.jpa.hibernate.ddl-auto=create-drop - - -server.port=8082 - - - -#sl4j -#logging.level.org.springframework.web=ERROR -#logging.level.com.stackroute=DEBUG -## Logging pattern for the console -#logging.pattern.console= "%d{yyyy-MM-dd HH:mm:ss} - %msg%n" -## Logging pattern for file -#logging.pattern.file= "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" -#logging.file=logs/application.log - -spring.application.name=event-service -eureka.client.serviceUrl.defaultZone=http://localhost:9091/eureka/ -eureka.client.register-with-eureka=true -eureka.client.fetch-registry=true - - - diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java deleted file mode 100644 index bbc9f3e..0000000 --- a/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.stackroute.nextevent; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class NexteventApplicationTests { - - @Test - public void contextLoads() { - } - -} - diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java deleted file mode 100644 index 5ee8530..0000000 --- a/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java +++ /dev/null @@ -1,105 +0,0 @@ -//package com.stackroute.nextevent.controller; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.stackroute.nextevent.exception.EventAlreadyExist; -//import com.stackroute.nextevent.exception.EventNotFound; -//import com.stackroute.nextevent.model.Category; -//import com.stackroute.nextevent.model.Event; -//import com.stackroute.nextevent.model.SubCategory; -//import com.stackroute.nextevent.model.Venu; -//import com.stackroute.nextevent.service.EventService; -//import org.junit.After; -//import org.junit.Before; -//import org.junit.Test; -//import org.junit.runner.RunWith; -//import org.junit.runners.Parameterized; -//import org.mockito.InjectMocks; -//import org.mockito.MockitoAnnotations; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -//import org.springframework.boot.test.mock.mockito.MockBean; -//import org.springframework.http.MediaType; -//import org.springframework.test.context.junit4.SpringRunner; -//import org.springframework.test.context.web.WebAppConfiguration; -//import org.springframework.test.web.servlet.MockMvc; -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -//import org.springframework.test.web.servlet.result.MockMvcResultHandlers; -//import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -//import org.springframework.test.web.servlet.setup.MockMvcBuilders; -// -//import java.text.DateFormat; -//import java.text.SimpleDateFormat; -//import java.time.LocalDate; -//import java.util.ArrayList; -//import java.util.Collection; -//import java.util.Date; -//import java.util.List; -// -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.Mockito.when; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -// -//@RunWith(SpringRunner.class) -//@WebMvcTest(EventController.class) -//public class EventControllerTest { -// @MockBean -// private EventService eventService; -// -// private MockMvc mockMvc; -// -// private Event event; -// -// @InjectMocks -// private EventController eventController; -// -// private List eventList; -// -// -// @Before -// public void setUp() throws Exception { -// eventList = new ArrayList<>(); -// MockitoAnnotations.initMocks(this); -// mockMvc = MockMvcBuilders.standaloneSetup(eventController).build(); -// Date dateFormat = new Date("1234/12/23 23:23:23"); -// Venu venu= new Venu("12","bangalore","bangalore",40); -// SubCategory subCategory= new SubCategory("1","music1"); -// List subCategoryList= new ArrayList<>(); -// subCategoryList.add(subCategory); -// Category category = new Category("12","music",subCategoryList,18); -// event = new Event( "123","123Efg","Event1","this is event1", dateFormat, dateFormat,dateFormat,venu,category,"imageurl"); -// eventList.add(event); -// } -// -// @After -// public void tearDown() throws Exception { -// -// } -// -// @Test -// public void testSaveEvent() throws Exception { -// when(eventService.saveEvent(any())).thenReturn(event); -// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/events") -// .contentType(MediaType.APPLICATION_JSON) -// .content(jsonToString(event))) -// .andExpect(MockMvcResultMatchers.status().isCreated()) -// .andDo(MockMvcResultHandlers.print()); -// } -// private static String jsonToString(final Object ob) throws JsonProcessingException { -// String result; -// -// try { -// ObjectMapper mapper = new ObjectMapper(); -// String jsonContent = mapper.writeValueAsString(ob); -// result = jsonContent; -// } catch(JsonProcessingException e) { -// result = "JSON processing error"; -// } -// -// return result; -// } -// -// -//} diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java deleted file mode 100644 index fc641c0..0000000 --- a/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.stackroute.nextevent.repository; - - -import com.stackroute.nextevent.model.Category; -import com.stackroute.nextevent.model.Event; -import com.stackroute.nextevent.model.SubCategory; -import com.stackroute.nextevent.model.Venu; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; - -@RunWith(SpringRunner.class) -//@DataMongoTest -@SpringBootTest -public class EventRepositoryTestIt { - - @Test - public void findByDescription() throws Exception { - assertEquals("Teaspoon", "Teaspoon"); - } -} - From d68980e1beb60c46dd4d2aa4ab4b369bc95cf467 Mon Sep 17 00:00:00 2001 From: sushantanshu Date: Tue, 12 Feb 2019 14:45:32 +0530 Subject: [PATCH 07/38] front-end --- eventbrite-service/.gitignore | 25 +++ eventbrite-service/Dockerfile | 6 + eventbrite-service/README.md | 0 eventbrite-service/pom.xml | 127 +++++++++++++++ .../nextevent/NexteventApplication.java | 16 ++ .../nextevent/config/SwaggerConfig.java | 22 +++ .../nextevent/controller/EventController.java | 96 ++++++++++++ .../controller/GlobalExceptionHandler.java | 55 +++++++ .../exception/EventAlreadyExist.java | 9 ++ .../nextevent/exception/EventNotFound.java | 11 ++ .../stackroute/nextevent/model/Category.java | 29 ++++ .../com/stackroute/nextevent/model/Event.java | 63 ++++++++ .../nextevent/model/SubCategory.java | 17 ++ .../com/stackroute/nextevent/model/Venu.java | 20 +++ .../nextevent/repository/EventRepository.java | 13 ++ .../nextevent/service/EventService.java | 17 ++ .../nextevent/service/EventServiceImpl.java | 145 ++++++++++++++++++ .../src/main/resources/application.properties | 27 ++++ .../nextevent/NexteventApplicationTests.java | 17 ++ .../controller/EventControllerTest.java | 105 +++++++++++++ .../repository/EventRepositoryTestIt.java | 37 +++++ 21 files changed, 857 insertions(+) create mode 100644 eventbrite-service/.gitignore create mode 100644 eventbrite-service/Dockerfile create mode 100644 eventbrite-service/README.md create mode 100644 eventbrite-service/pom.xml create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java create mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java create mode 100644 eventbrite-service/src/main/resources/application.properties create mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java create mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java create mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java diff --git a/eventbrite-service/.gitignore b/eventbrite-service/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/eventbrite-service/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/eventbrite-service/Dockerfile b/eventbrite-service/Dockerfile new file mode 100644 index 0000000..9ead3ad --- /dev/null +++ b/eventbrite-service/Dockerfile @@ -0,0 +1,6 @@ +FROM openjdk:11 + +ADD ./target/nextevent-0.0.1-SNAPSHOT.jar /event/src/nextevent-0.0.1-SNAPSHOT.jar +EXPOSE 8082 +WORKDIR event/src +ENTRYPOINT ["java","-jar","nextevent-0.0.1-SNAPSHOT.jar"] diff --git a/eventbrite-service/README.md b/eventbrite-service/README.md new file mode 100644 index 0000000..e69de29 diff --git a/eventbrite-service/pom.xml b/eventbrite-service/pom.xml new file mode 100644 index 0000000..c4816bc --- /dev/null +++ b/eventbrite-service/pom.xml @@ -0,0 +1,127 @@ + + + 4.0.0 + + com.stackroute + syscop + 0.0.1-SNAPSHOT + ../pom.xml + + com.stackroute + eventbrite-service + 0.0.1-SNAPSHOT + eventbrite-service + nextEvent project in Spring Boot + + + UTF-8 + UTF-8 + 1.8 + ${java.version} + ${java.version} + + + + + + + + + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + io.springfox + springfox-swagger2 + 2.9.2 + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + io.springfox + springfox-swagger-ui + 2.9.2 + + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.projectlombok + lombok-maven + 1.18.4.0 + pom + + + + org.projectlombok + lombok + 1.18.4 + provided + + + org.springframework.cloud + spring-cloud-commons + 2.0.1.RELEASE + + + + + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + + diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java new file mode 100644 index 0000000..36db246 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java @@ -0,0 +1,16 @@ +package com.stackroute.nextevent; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableEurekaClient +public class NexteventApplication { + + public static void main(String[] args) { + SpringApplication.run(NexteventApplication.class, args); + } + +} + diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java new file mode 100644 index 0000000..4eeb351 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java @@ -0,0 +1,22 @@ +package com.stackroute.nextevent.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket customImplementation(){ + return new Docket(DocumentationType.SWAGGER_2) + .select() + .apis(RequestHandlerSelectors.basePackage("com.stackroute.nextevent")) + .build(); + } + } + diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java new file mode 100644 index 0000000..da9b5b2 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java @@ -0,0 +1,96 @@ +package com.stackroute.nextevent.controller; + +import com.stackroute.nextevent.exception.EventAlreadyExist; +import com.stackroute.nextevent.exception.EventNotFound; +import com.stackroute.nextevent.model.Event; +import com.stackroute.nextevent.service.EventService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("api/v1") +public class EventController { + private static Logger log = LoggerFactory.getLogger(EventController.class); + + private ResponseEntity responseEntity; + private EventService eventService; + + @Autowired + private EventController(final EventService eventService) { + this.eventService = eventService; + } + + @GetMapping("events") + public ResponseEntity getAllEvent() throws EventNotFound { + log.info("getAllEvevnt hit"); + responseEntity = new ResponseEntity(eventService.getAllEvent(), HttpStatus.OK); + return responseEntity; + } + + @PostMapping("events") + public ResponseEntity saveEvent(@RequestBody Event event) throws EventAlreadyExist{ + eventService.saveEvent(event); + responseEntity = new ResponseEntity(event, HttpStatus.CREATED); + + return responseEntity; + } + + @DeleteMapping("events/{id}") + public ResponseEntity deleteEvent(@PathVariable("id") String id) throws EventNotFound { + eventService.deleteEvent(id); + log.info("event mapping hit event deleted======"); + responseEntity = new ResponseEntity("Event deleted successfully", HttpStatus.OK); + return responseEntity; + } + + + @PutMapping("events/{id}") + public ResponseEntity updateEvent(@PathVariable("id") String id,@RequestBody Event event) throws EventNotFound { + log.info("upadte event hit"); + Event event1= eventService.updateEvent(event,id); + log.info("updated event+++++"+event1); + return responseEntity; + } + + @GetMapping("event/{id}") + public ResponseEntity getEventById(@PathVariable("id") String id) throws EventNotFound { + log.info("getEvent hit by Id"); + responseEntity = new ResponseEntity(eventService.getEventById(id), HttpStatus.OK); + return responseEntity; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java new file mode 100644 index 0000000..fcf5f58 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java @@ -0,0 +1,55 @@ +package com.stackroute.nextevent.controller; + +import com.stackroute.nextevent.exception.EventAlreadyExist; +import com.stackroute.nextevent.exception.EventNotFound; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + + +@ControllerAdvice +//@Log4j +public class GlobalExceptionHandler { + private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + @ResponseStatus(value = HttpStatus.CONFLICT,reason = "Event Already Exists") + @ExceptionHandler(EventAlreadyExist.class) + public void handleEventAlreadyExists(EventAlreadyExist e) + { + log.error("Event alredy Exists"); + } + + @ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "Event Not Found") + @ExceptionHandler(EventNotFound.class) + public void handleEventNotFound() + { + log.error("Event Not Found"); + } + + @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "Something went wrong ") + public void handleException(Exception e) + { + log.error("Try!!! After sometime"); + } +// @ResponseStatus(value= HttpStatus.BAD_REQUEST, reason="Not all mandatory fields are filled") +// @ExceptionHandler(EmptyFieldException.class) +// public void handleEmptyFieldException(EmptyFieldException e){ +// log.error("Not all mandatory fields are filled", e); +// } +// +// @ResponseStatus(value= HttpStatus.CONFLICT, reason="User already exists") +// @ExceptionHandler(UserAlreadyExistsException.class) +// public void handleUserAlreadyExistsException(UserAlreadyExistsException e){ +// log.error("User already exists", e); +// } +// +// @ResponseStatus(value= HttpStatus.NOT_FOUND, reason="User does not exist") +// @ExceptionHandler(UserDoesNotExistException.class) +// public void handleUserDoesNotExistException(UserDoesNotExistException e){ +// log.error("User does not exist", e); +// } + + +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java new file mode 100644 index 0000000..ca55e5c --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java @@ -0,0 +1,9 @@ +package com.stackroute.nextevent.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.CONFLICT, reason = "Event already exists on server") +public class EventAlreadyExist extends Exception { + public EventAlreadyExist(String message){super(message);} +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java new file mode 100644 index 0000000..34369e4 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java @@ -0,0 +1,11 @@ +package com.stackroute.nextevent.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Specified Event Not Found on server") +public class EventNotFound extends Exception { + + public EventNotFound(String message){super(message);} + +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java new file mode 100644 index 0000000..d2e3706 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java @@ -0,0 +1,29 @@ +package com.stackroute.nextevent.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Category { + private String categoryId; + private String name; + private List subCategory; + private int ageRestriction; + + @Override + public String toString() { + return "Category{" + + "categoryId='" + categoryId + '\'' + + ", name='" + name + '\'' + + ", subCategory=" + subCategory + + ", ageRestriction=" + ageRestriction + + '}'; + } +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java new file mode 100644 index 0000000..778a43d --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java @@ -0,0 +1,63 @@ +package com.stackroute.nextevent.model; + +import javax.validation.constraints.NotNull; +import java.util.Date; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + +@Document +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Event { + + @Id + private String id; + + @NotNull + private String eventId; + + @NotNull + private String name; + + @NotNull + private String description; + + private Date created; + + //@NotNull + private Date start; + + //@NotNull + private Date end; + + private Venu venu; + + private Category category; + + @NotNull + private String imageUrl; + + @Override + public String toString() { + return "Event{" + + "id='" + id + '\'' + + ", eventId='" + eventId + '\'' + + ", name='" + name + '\'' + + ", description='" + description + '\'' + + ", created=" + created + + ", start=" + start + + ", end=" + end + + ", venu=" + venu + + ", category=" + category + + ", imageUrl='" + imageUrl + '\'' + + '}'; + } +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java new file mode 100644 index 0000000..59bca65 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java @@ -0,0 +1,17 @@ +package com.stackroute.nextevent.model; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class SubCategory { + String subcategoryId; + String name; + + +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java new file mode 100644 index 0000000..aa05a20 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java @@ -0,0 +1,20 @@ +package com.stackroute.nextevent.model; + + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Venu { + String venuId; + String address; + String name; + int capacity; + + +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java new file mode 100644 index 0000000..74e6132 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java @@ -0,0 +1,13 @@ +package com.stackroute.nextevent.repository; + +import com.stackroute.nextevent.model.Event; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface EventRepository extends MongoRepository { + + Event findByEventId(String id); + +} + diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java new file mode 100644 index 0000000..717f8db --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java @@ -0,0 +1,17 @@ +package com.stackroute.nextevent.service; + +import com.stackroute.nextevent.exception.EventAlreadyExist; +import com.stackroute.nextevent.exception.EventNotFound; +import com.stackroute.nextevent.model.Event; + +import java.util.List; + +public interface EventService { + + Event saveEvent(Event event) throws EventAlreadyExist; + boolean deleteEvent(String id) throws EventNotFound; + Event updateEvent(Event event,String id) throws EventNotFound; + Event getEventById(String id) throws EventNotFound; + List getAllEvent() throws EventNotFound; + +} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java new file mode 100644 index 0000000..ff536b8 --- /dev/null +++ b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java @@ -0,0 +1,145 @@ +package com.stackroute.nextevent.service; + +import com.stackroute.nextevent.exception.EventAlreadyExist; +import com.stackroute.nextevent.exception.EventNotFound; +import com.stackroute.nextevent.model.Event; +import com.stackroute.nextevent.repository.EventRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +public class EventServiceImpl implements EventService { + + private static Logger log = LoggerFactory.getLogger(EventServiceImpl.class); + private EventRepository eventRepository; + // private Event event; + + @Autowired + EventServiceImpl(EventRepository eventRepository) + { + this.eventRepository = eventRepository; + } + + @Override + public Event saveEvent(Event event) throws EventAlreadyExist { + //return null; + Optional optional = eventRepository.findById(event.getId()); + if(optional.isPresent()) + throw new EventAlreadyExist("Event already exists"); + else + return eventRepository.save(event); + + } +// log.info("we are in SaveEvent"); +// System.out.println("++++++++++++++++++++++++++++"+eventRepository.findById(event.getId())); +// Optional optional = eventRepository.findById(event.getId()); +// if((optional.isPresent())){ +// throw new EventAlreadyExist("Event already exists"); +// } +// else { +// log.info("Event is+++++++++++" + event); +// return eventRepository.save(event); +// } + // } + + @Override + public boolean deleteEvent(String id) throws EventNotFound { + //return false; + // boolean status = false; + Optional optional = eventRepository.findById(id); + log.info("check event+++++++"+optional); + if(optional.isPresent()) { + eventRepository.deleteById(id); + System.out.println("Id is"+id); + // eventRepository.deleteById(id); + return true; + } + else{ + throw new EventNotFound("Event not Found"); + } + + //return status; +// Event event = eventRepository.getOne(id); +// if(event!=null) +// { +// eventRepository.delete(event); +// return true; +// } +// else { +// throw new EventNotFound("Event not found with this id"); +// } + + } + + @Override + public Event updateEvent(Event event, String id) throws EventNotFound { + //return null; + //return null; + Optional optional = eventRepository.findById(id); + if(optional.isPresent()){ + event = eventRepository.findById(id).get(); + event.setCreated(event.getCreated()); + event.setDescription(event.getDescription()); + event.setEnd(event.getEnd()); + event.setStart(event.getStart()); + event.setImageUrl(event.getImageUrl()); + event.setName(event.getName()); + event.setCategory(event.getCategory()); + log.info("event is======"+event); +// System.out.println(event); +// System.out.println("updated evet+++++++++++"+event); + log.info("updated event Saved"); + eventRepository.save(event); + // System.out.println("updated ++++++"+eventRepository.save(event)); + // muzixRepository.save(track); + //return updatedEvent; + + } + else + { + throw new EventNotFound("Event not found"); + } + return event; +// Event eventToBeUpdated = eventRepository.getOne(event.getId()); +// if(event!=null) +// { +// eventToBeUpdated.setCreated(event.getCreated()); +// eventToBeUpdated.setDescription(event.getDescription()); +// eventToBeUpdated.setEnd(event.getEnd()); +// eventToBeUpdated.setStart(event.getStart()); +// eventToBeUpdated.setImageUrl(event.getImageUrl()); +// eventToBeUpdated.setName(event.getName()); +// return eventRepository.save(eventToBeUpdated); +// +// } +// else { +// throw new EventNotFound("Event not found with this id"); +// } + } + + @Override + public Event getEventById(String id) throws EventNotFound { + //return null; + Optional optional = eventRepository.findById(id); + if(optional.isPresent()) { + log.info("inside if evntId found"); + return eventRepository.findById(id).get(); + } + else{ + throw new EventNotFound("Event not Found"); + } + + } + + @Override + public List getAllEvent() throws EventNotFound { + //return null; + return eventRepository.findAll(); + + } +} diff --git a/eventbrite-service/src/main/resources/application.properties b/eventbrite-service/src/main/resources/application.properties new file mode 100644 index 0000000..68822ed --- /dev/null +++ b/eventbrite-service/src/main/resources/application.properties @@ -0,0 +1,27 @@ + +#spring.datasource.url=jdbc:mysql://localhost:3306/eventdata +#spring.datasource.username=root +#spring.datasource.password=123 +#spring.jpa.hibernate.ddl-auto=create-drop + + +server.port=8082 + + + +#sl4j +#logging.level.org.springframework.web=ERROR +#logging.level.com.stackroute=DEBUG +## Logging pattern for the console +#logging.pattern.console= "%d{yyyy-MM-dd HH:mm:ss} - %msg%n" +## Logging pattern for file +#logging.pattern.file= "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" +#logging.file=logs/application.log + +spring.application.name=event-service +eureka.client.serviceUrl.defaultZone=http://localhost:9091/eureka/ +eureka.client.register-with-eureka=true +eureka.client.fetch-registry=true + + + diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java new file mode 100644 index 0000000..bbc9f3e --- /dev/null +++ b/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java @@ -0,0 +1,17 @@ +package com.stackroute.nextevent; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class NexteventApplicationTests { + + @Test + public void contextLoads() { + } + +} + diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java new file mode 100644 index 0000000..5ee8530 --- /dev/null +++ b/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java @@ -0,0 +1,105 @@ +//package com.stackroute.nextevent.controller; +// +//import com.fasterxml.jackson.core.JsonProcessingException; +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.stackroute.nextevent.exception.EventAlreadyExist; +//import com.stackroute.nextevent.exception.EventNotFound; +//import com.stackroute.nextevent.model.Category; +//import com.stackroute.nextevent.model.Event; +//import com.stackroute.nextevent.model.SubCategory; +//import com.stackroute.nextevent.model.Venu; +//import com.stackroute.nextevent.service.EventService; +//import org.junit.After; +//import org.junit.Before; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.junit.runners.Parameterized; +//import org.mockito.InjectMocks; +//import org.mockito.MockitoAnnotations; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +//import org.springframework.boot.test.mock.mockito.MockBean; +//import org.springframework.http.MediaType; +//import org.springframework.test.context.junit4.SpringRunner; +//import org.springframework.test.context.web.WebAppConfiguration; +//import org.springframework.test.web.servlet.MockMvc; +//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +//import org.springframework.test.web.servlet.result.MockMvcResultHandlers; +//import org.springframework.test.web.servlet.result.MockMvcResultMatchers; +//import org.springframework.test.web.servlet.setup.MockMvcBuilders; +// +//import java.text.DateFormat; +//import java.text.SimpleDateFormat; +//import java.time.LocalDate; +//import java.util.ArrayList; +//import java.util.Collection; +//import java.util.Date; +//import java.util.List; +// +//import static org.mockito.ArgumentMatchers.any; +//import static org.mockito.Mockito.when; +//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +// +//@RunWith(SpringRunner.class) +//@WebMvcTest(EventController.class) +//public class EventControllerTest { +// @MockBean +// private EventService eventService; +// +// private MockMvc mockMvc; +// +// private Event event; +// +// @InjectMocks +// private EventController eventController; +// +// private List eventList; +// +// +// @Before +// public void setUp() throws Exception { +// eventList = new ArrayList<>(); +// MockitoAnnotations.initMocks(this); +// mockMvc = MockMvcBuilders.standaloneSetup(eventController).build(); +// Date dateFormat = new Date("1234/12/23 23:23:23"); +// Venu venu= new Venu("12","bangalore","bangalore",40); +// SubCategory subCategory= new SubCategory("1","music1"); +// List subCategoryList= new ArrayList<>(); +// subCategoryList.add(subCategory); +// Category category = new Category("12","music",subCategoryList,18); +// event = new Event( "123","123Efg","Event1","this is event1", dateFormat, dateFormat,dateFormat,venu,category,"imageurl"); +// eventList.add(event); +// } +// +// @After +// public void tearDown() throws Exception { +// +// } +// +// @Test +// public void testSaveEvent() throws Exception { +// when(eventService.saveEvent(any())).thenReturn(event); +// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/events") +// .contentType(MediaType.APPLICATION_JSON) +// .content(jsonToString(event))) +// .andExpect(MockMvcResultMatchers.status().isCreated()) +// .andDo(MockMvcResultHandlers.print()); +// } +// private static String jsonToString(final Object ob) throws JsonProcessingException { +// String result; +// +// try { +// ObjectMapper mapper = new ObjectMapper(); +// String jsonContent = mapper.writeValueAsString(ob); +// result = jsonContent; +// } catch(JsonProcessingException e) { +// result = "JSON processing error"; +// } +// +// return result; +// } +// +// +//} diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java new file mode 100644 index 0000000..fc641c0 --- /dev/null +++ b/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java @@ -0,0 +1,37 @@ +package com.stackroute.nextevent.repository; + + +import com.stackroute.nextevent.model.Category; +import com.stackroute.nextevent.model.Event; +import com.stackroute.nextevent.model.SubCategory; +import com.stackroute.nextevent.model.Venu; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +//@DataMongoTest +@SpringBootTest +public class EventRepositoryTestIt { + + @Test + public void findByDescription() throws Exception { + assertEquals("Teaspoon", "Teaspoon"); + } +} + From 220189a875838d3d873de662ab9ed7b850db8979 Mon Sep 17 00:00:00 2001 From: sushantanshu Date: Thu, 14 Feb 2019 12:36:28 +0530 Subject: [PATCH 08/38] front-end --- frontend-syscop/package-lock.json | 442 ++++++++- frontend-syscop/package.json | 2 + frontend-syscop/src/app/app-routing.module.ts | 8 +- frontend-syscop/src/app/app.module.ts | 16 +- .../app/chart-four/chart-four.component.css | 0 .../app/chart-four/chart-four.component.html | 1 + .../chart-four/chart-four.component.spec.ts | 25 + .../app/chart-four/chart-four.component.ts | 15 + .../src/app/chart-one/chart-one.component.css | 0 .../app/chart-one/chart-one.component.html | 1 + .../app/chart-one/chart-one.component.spec.ts | 25 + .../src/app/chart-one/chart-one.component.ts | 40 + .../app/chart-three/chart-three.component.css | 0 .../chart-three/chart-three.component.html | 1 + .../chart-three/chart-three.component.spec.ts | 25 + .../app/chart-three/chart-three.component.ts | 57 ++ .../src/app/chart-two/chart-two.component.css | 0 .../app/chart-two/chart-two.component.html | 1 + .../app/chart-two/chart-two.component.spec.ts | 25 + .../src/app/chart-two/chart-two.component.ts | 41 + .../src/app/dashboard/dashboard.component.css | 21 + .../app/dashboard/dashboard.component.html | 29 +- .../app/dashboard/dashboard.component.spec.ts | 25 +- .../src/app/dashboard/dashboard.component.ts | 53 +- .../src/app/home/home.component.css | 4 +- .../src/app/home/home.component.html | 16 +- .../src/app/home/home.component.ts | 2 - .../src/canvasjs-2.3.1/canvasjs.min.js | 911 +++++++++++++++++ .../src/canvasjs-2.3.1/canvasjs.react.js | 48 + .../examples/01-overview/animated-chart.html | 43 + .../01-overview/chart-from-json-data.html | 50 + .../chart-with-axis-scale-breaks.html | 51 + .../01-overview/chart-with-crosshair.html | 72 ++ .../chart-with-custom-legend-chart.html | 233 +++++ .../01-overview/chart-with-image-overlay.html | 108 ++ .../chart-with-index-data-label.html | 46 + .../chart-with-inverted-reversed-axis.html | 46 + .../chart-with-logarithmic-axis.html | 128 +++ .../chart-with-secondary-axis.html | 87 ++ .../01-overview/chart-with-zoom-pan.html | 40 + .../examples/01-overview/dynamic-chart.html | 57 ++ .../interactive-draggable-chart.html | 115 +++ .../01-overview/multi-series-chart.html | 94 ++ .../examples/01-overview/null-data-chart.html | 62 ++ .../01-overview/performance-demo.html | 43 + .../dashed-line-chart.html | 102 ++ .../dynamic-spline-chart.html | 56 ++ .../line-chart-with-axis-scale-breaks.html | 71 ++ .../line-chart-with-data-markers.html | 53 + .../line-chart-with-logarithmic-axis.html | 107 ++ .../line-chart-with-multiple-axis.html | 125 +++ .../line-chart-with-zoom-pan.html | 46 + .../line-chart.html | 44 + .../multi-series-line-chart.html | 257 +++++ .../multi-series-spline-chart.html | 171 ++++ .../multi-series-step-line-chart.html | 67 ++ .../spline-chart-with-legends.html | 123 +++ .../spline-chart-with-secondary-axis.html | 100 ++ .../spline-chart.html | 55 ++ .../step-line-chart.html | 57 ++ .../area-chart.html | 50 + .../multi-series-area-chart.html | 73 ++ .../multi-series-range-area-chart.html | 127 +++ .../multi-series-spline-area-chart.html | 113 +++ .../range-area-chart.html | 72 ++ .../range-spline-area-chart.html | 51 + .../spline-area-chart.html | 55 ++ ...ed-area-100-chart-with-date-time-axis.html | 120 +++ .../stacked-area-100-chart.html | 78 ++ .../stacked-area-chart.html | 68 ++ .../step-area-chart.html | 42 + .../bar-chart-with-axis-scale-break.html | 58 ++ .../bar-chart.html | 58 ++ .../column-chart-with-multiple-axis.html | 83 ++ .../column-chart.html | 43 + .../multi-series-bar-chart.html | 104 ++ .../multi-series-range-column-chart.html | 89 ++ .../multi-series-waterfall-chart.html | 98 ++ .../range-bar-chart.html | 49 + .../range-column-chart.html | 49 + .../stacked-bar-100-chart.html | 76 ++ .../stacked-bar-chart.html | 126 +++ .../stacked-column-100-chart.html | 103 ++ .../stacked-column-chart.html | 113 +++ .../waterfall-chart-with-custom-color.html | 51 + .../waterfall-chart.html | 48 + .../doughnut-Chart.html | 40 + ...ughnut-chart-with-custom-inner-radius.html | 55 ++ .../funnel-chart-with-custom-neck.html | 52 + .../funnel-chart.html | 52 + .../inverted-reversed-funnel-chart.html | 54 + .../pie-chart-with-custom-radius.html | 51 + .../pie-chart-with-legends.html | 52 + .../pie-chart.html | 36 + ...mid-chart-where-area-represents-value.html | 39 + ...-chart-with-index-label-placed-Inside.html | 40 + .../pyramid-chart.html | 40 + .../candlestick-line-chart.html | 108 ++ .../multi-series-candlestick-chart.html | 90 ++ .../ohlc-chart-from-json-data.html | 58 ++ .../ohlc-stock-chart.html | 54 + .../bubble-chart-with-data-marker.html | 59 ++ .../bubble-chart-with-zoom-pan.html | 60 ++ .../bubble-chart.html | 69 ++ .../multi-series-scatter-point-chart.html | 73 ++ ...catter-point-chart-with-custom-marker.html | 129 +++ .../scatter-point-chart.html | 63 ++ ...x-and-whisker-chart-with-custom-color.html | 43 + .../box-and-whisker-chart-with-outliers.html | 81 ++ .../box-and-whisker-chart.html | 43 + .../column-line-area-chart.html | 122 +++ .../error-bar-chart.html | 54 + .../09-combination-charts/error-chart.html | 58 ++ .../error-line-chart.html | 73 ++ .../ohlc-line-chart.html | 106 ++ .../pareto-chart-with-index-data-label.html | 68 ++ .../09-combination-charts/pareto-chart.html | 66 ++ .../range-area-line-chart.html | 108 ++ .../dynamic-column-chart.html | 54 + .../10-dynamic-charts/dynamic-line-chart.html | 58 ++ .../dynamic-multi-series-chart.html | 113 +++ .../11-integration/jquery-charts.html | 42 + .../jquery-line-chart-with-zoom-pan.html | 49 + .../jquery-resizable-chart.html | 55 ++ .../jquery-spline-area-chart-in-tab.html | 98 ++ ...jquery-spline-chart-with-image-export.html | 41 + .../src/canvasjs-2.3.1/instruction.txt | 4 + .../src/canvasjs-2.3.1/jquery.canvasjs.min.js | 922 ++++++++++++++++++ .../src/canvasjs-2.3.1/license.txt | 9 + frontend-syscop/src/index.html | 2 + 130 files changed, 9994 insertions(+), 89 deletions(-) create mode 100644 frontend-syscop/src/app/chart-four/chart-four.component.css create mode 100644 frontend-syscop/src/app/chart-four/chart-four.component.html create mode 100644 frontend-syscop/src/app/chart-four/chart-four.component.spec.ts create mode 100644 frontend-syscop/src/app/chart-four/chart-four.component.ts create mode 100644 frontend-syscop/src/app/chart-one/chart-one.component.css create mode 100644 frontend-syscop/src/app/chart-one/chart-one.component.html create mode 100644 frontend-syscop/src/app/chart-one/chart-one.component.spec.ts create mode 100644 frontend-syscop/src/app/chart-one/chart-one.component.ts create mode 100644 frontend-syscop/src/app/chart-three/chart-three.component.css create mode 100644 frontend-syscop/src/app/chart-three/chart-three.component.html create mode 100644 frontend-syscop/src/app/chart-three/chart-three.component.spec.ts create mode 100644 frontend-syscop/src/app/chart-three/chart-three.component.ts create mode 100644 frontend-syscop/src/app/chart-two/chart-two.component.css create mode 100644 frontend-syscop/src/app/chart-two/chart-two.component.html create mode 100644 frontend-syscop/src/app/chart-two/chart-two.component.spec.ts create mode 100644 frontend-syscop/src/app/chart-two/chart-two.component.ts create mode 100644 frontend-syscop/src/canvasjs-2.3.1/canvasjs.min.js create mode 100644 frontend-syscop/src/canvasjs-2.3.1/canvasjs.react.js create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/animated-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-from-json-data.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-axis-scale-breaks.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-crosshair.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-custom-legend-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-image-overlay.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-index-data-label.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-inverted-reversed-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-logarithmic-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-secondary-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-zoom-pan.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/dynamic-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/interactive-draggable-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/multi-series-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/null-data-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/performance-demo.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dashed-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dynamic-spline-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-axis-scale-breaks.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-data-markers.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-logarithmic-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-multiple-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-zoom-pan.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-spline-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-step-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-legends.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-secondary-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/step-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-range-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-spline-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-spline-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/spline-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart-with-date-time-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/step-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart-with-axis-scale-break.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart-with-multiple-axis.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-bar-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-range-column-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-waterfall-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-bar-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-column-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-100-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-100-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart-with-custom-color.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-Chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-chart-with-custom-inner-radius.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart-with-custom-neck.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/inverted-reversed-funnel-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-custom-radius.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-legends.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-where-area-represents-value.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-with-index-label-placed-Inside.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/candlestick-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/multi-series-candlestick-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-chart-from-json-data.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-stock-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-data-marker.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-zoom-pan.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/multi-series-scatter-point-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart-with-custom-marker.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-custom-color.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-outliers.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/column-line-area-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-bar-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/ohlc-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart-with-index-data-label.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/range-area-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-column-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-line-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-multi-series-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-charts.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-line-chart-with-zoom-pan.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-resizable-chart.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-area-chart-in-tab.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-chart-with-image-export.html create mode 100644 frontend-syscop/src/canvasjs-2.3.1/instruction.txt create mode 100644 frontend-syscop/src/canvasjs-2.3.1/jquery.canvasjs.min.js create mode 100644 frontend-syscop/src/canvasjs-2.3.1/license.txt diff --git a/frontend-syscop/package-lock.json b/frontend-syscop/package-lock.json index aa646ba..6522574 100644 --- a/frontend-syscop/package-lock.json +++ b/frontend-syscop/package-lock.json @@ -1205,8 +1205,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", @@ -1470,7 +1469,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, "requires": { "chalk": "^1.1.3", "esutils": "^2.0.2", @@ -1480,14 +1478,12 @@ "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, "requires": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -1499,8 +1495,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" } } }, @@ -1528,20 +1523,382 @@ } } }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, "babel-messages": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, "requires": { "babel-runtime": "^6.22.0" } }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + } + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-es2015": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", + "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.24.1", + "babel-plugin-transform-es2015-classes": "^6.24.1", + "babel-plugin-transform-es2015-computed-properties": "^6.24.1", + "babel-plugin-transform-es2015-destructuring": "^6.22.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", + "babel-plugin-transform-es2015-for-of": "^6.22.0", + "babel-plugin-transform-es2015-function-name": "^6.24.1", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", + "babel-plugin-transform-es2015-modules-umd": "^6.24.1", + "babel-plugin-transform-es2015-object-super": "^6.24.1", + "babel-plugin-transform-es2015-parameters": "^6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.24.1" + } + }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, "requires": { "core-js": "^2.4.0", "regenerator-runtime": "^0.11.0" @@ -1551,7 +1908,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, "requires": { "babel-runtime": "^6.26.0", "babel-traverse": "^6.26.0", @@ -1564,7 +1920,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, "requires": { "babel-code-frame": "^6.26.0", "babel-messages": "^6.23.0", @@ -1581,7 +1936,6 @@ "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, "requires": { "babel-runtime": "^6.26.0", "esutils": "^2.0.2", @@ -1592,8 +1946,7 @@ "babylon": { "version": "6.18.0", "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" }, "backo2": { "version": "1.0.2", @@ -2078,6 +2431,11 @@ "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==", "dev": true }, + "canvasjs": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/canvasjs/-/canvasjs-1.8.1.tgz", + "integrity": "sha1-aYpOAC4zbOg+8vkQGmvSdrp2V6M=" + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2997,7 +3355,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -3474,8 +3831,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.8.1", @@ -3552,8 +3908,7 @@ "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" }, "etag": { "version": "1.8.1", @@ -4912,8 +5267,7 @@ "globals": { "version": "9.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" }, "globby": { "version": "7.1.1", @@ -5007,7 +5361,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5536,7 +5889,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "requires": { "loose-envify": "^1.0.0" } @@ -6151,8 +6503,7 @@ "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" }, "js-yaml": { "version": "3.12.1", @@ -6525,8 +6876,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash.assign": { "version": "4.2.0", @@ -6600,7 +6950,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -7069,8 +7418,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "multicast-dns": { "version": "6.2.3", @@ -8125,6 +8473,11 @@ "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", "dev": true }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -8603,14 +8956,22 @@ "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", - "dev": true + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } }, "regex-cache": { "version": "0.4.4", @@ -8645,14 +9006,12 @@ "regjsgen": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" }, "regjsparser": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, "requires": { "jsesc": "~0.5.0" }, @@ -8660,8 +9019,7 @@ "jsesc": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" } } }, @@ -9826,7 +10184,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, "requires": { "ansi-regex": "^2.0.0" } @@ -10185,8 +10542,7 @@ "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" }, "to-object-path": { "version": "0.3.0", diff --git a/frontend-syscop/package.json b/frontend-syscop/package.json index 0008982..29f6128 100644 --- a/frontend-syscop/package.json +++ b/frontend-syscop/package.json @@ -22,6 +22,8 @@ "@angular/platform-browser": "~7.2.0", "@angular/platform-browser-dynamic": "~7.2.0", "@angular/router": "~7.2.0", + "babel-preset-es2015": "^6.24.1", + "canvasjs": "^1.8.1", "core-js": "^2.5.4", "d3": "^5.9.1", "hammerjs": "^2.0.8", diff --git a/frontend-syscop/src/app/app-routing.module.ts b/frontend-syscop/src/app/app-routing.module.ts index 692ffdb..bf9bae6 100644 --- a/frontend-syscop/src/app/app-routing.module.ts +++ b/frontend-syscop/src/app/app-routing.module.ts @@ -10,8 +10,12 @@ const routes: Routes = [ {path: '', component: InitialViewComponent}, {path: 'login', component: LoginComponent}, {path: 'signup', component: SignupComponent}, - {path: 'home', component: HomeComponent}, - {path: 'home/dashboard', component: DashboardComponent} + { path: 'home', + component: HomeComponent, + children: [ + {path: 'dashboard', component: DashboardComponent }, + ] + }, ]; @NgModule({ diff --git a/frontend-syscop/src/app/app.module.ts b/frontend-syscop/src/app/app.module.ts index 16b2164..1379dee 100644 --- a/frontend-syscop/src/app/app.module.ts +++ b/frontend-syscop/src/app/app.module.ts @@ -1,7 +1,7 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { MatTabsModule, MatSidenavModule, MatToolbarModule, MatIconModule, - MatButtonModule, MatListModule, MatNativeDateModule } from '@angular/material'; + MatButtonModule, MatListModule, MatNativeDateModule, MatGridListModule, MatMenuModule } from '@angular/material'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent, } from './app.component'; @@ -20,6 +20,10 @@ import { HttpClientModule } from '@angular/common/http'; import { FlexLayoutModule } from '@angular/flex-layout'; import { MatBadgeModule } from '@angular/material/badge'; import { DashboardComponent } from './dashboard/dashboard.component'; +import { ChartOneComponent } from './chart-one/chart-one.component'; +import { ChartTwoComponent } from './chart-two/chart-two.component'; +import { ChartThreeComponent } from './chart-three/chart-three.component'; +import { ChartFourComponent } from './chart-four/chart-four.component'; @@ -31,6 +35,10 @@ import { DashboardComponent } from './dashboard/dashboard.component'; InitialViewComponent, HomeComponent, DashboardComponent, + ChartOneComponent, + ChartTwoComponent, + ChartThreeComponent, + ChartFourComponent, ], imports: [ BrowserModule, @@ -52,10 +60,12 @@ import { DashboardComponent } from './dashboard/dashboard.component'; MatDatepickerModule, MatNativeDateModule, FlexLayoutModule, - MatBadgeModule + MatBadgeModule, + MatGridListModule, + MatMenuModule ], providers: [], - bootstrap: [AppComponent] + bootstrap: [AppComponent ] }) export class AppModule { } diff --git a/frontend-syscop/src/app/chart-four/chart-four.component.css b/frontend-syscop/src/app/chart-four/chart-four.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend-syscop/src/app/chart-four/chart-four.component.html b/frontend-syscop/src/app/chart-four/chart-four.component.html new file mode 100644 index 0000000..195e602 --- /dev/null +++ b/frontend-syscop/src/app/chart-four/chart-four.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/frontend-syscop/src/app/chart-four/chart-four.component.spec.ts b/frontend-syscop/src/app/chart-four/chart-four.component.spec.ts new file mode 100644 index 0000000..55e1de3 --- /dev/null +++ b/frontend-syscop/src/app/chart-four/chart-four.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChartFourComponent } from './chart-four.component'; + +describe('ChartFourComponent', () => { + let component: ChartFourComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChartFourComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartFourComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/chart-four/chart-four.component.ts b/frontend-syscop/src/app/chart-four/chart-four.component.ts new file mode 100644 index 0000000..34268e0 --- /dev/null +++ b/frontend-syscop/src/app/chart-four/chart-four.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-chart-four', + templateUrl: './chart-four.component.html', + styleUrls: ['./chart-four.component.css'] +}) +export class ChartFourComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/frontend-syscop/src/app/chart-one/chart-one.component.css b/frontend-syscop/src/app/chart-one/chart-one.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend-syscop/src/app/chart-one/chart-one.component.html b/frontend-syscop/src/app/chart-one/chart-one.component.html new file mode 100644 index 0000000..1b3a692 --- /dev/null +++ b/frontend-syscop/src/app/chart-one/chart-one.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/frontend-syscop/src/app/chart-one/chart-one.component.spec.ts b/frontend-syscop/src/app/chart-one/chart-one.component.spec.ts new file mode 100644 index 0000000..b737798 --- /dev/null +++ b/frontend-syscop/src/app/chart-one/chart-one.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChartOneComponent } from './chart-one.component'; + +describe('ChartOneComponent', () => { + let component: ChartOneComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChartOneComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartOneComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/chart-one/chart-one.component.ts b/frontend-syscop/src/app/chart-one/chart-one.component.ts new file mode 100644 index 0000000..70c5b6f --- /dev/null +++ b/frontend-syscop/src/app/chart-one/chart-one.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit } from '@angular/core'; +import * as CanvasJS from 'src/canvasjs-2.3.1/canvasjs.min.js'; + +@Component({ + selector: 'app-chart-one', + templateUrl: './chart-one.component.html', + styleUrls: ['./chart-one.component.css'] +}) +export class ChartOneComponent implements OnInit { + + constructor() { } + + ngOnInit() { + const chart = new CanvasJS.Chart('chartContainer1', { + animationEnabled: true, + exportEnabled: true, + title: { + text: 'Basic Column Chart in Angular' + }, + data: [{ + type: 'column', + dataPoints: [ + { y: 71, label: 'Apple' }, + { y: 55, label: 'Mango' }, + { y: 50, label: 'Orange' }, + { y: 65, label: 'Banana' }, + { y: 95, label: 'Pineapple' }, + { y: 68, label: 'Pears' }, + { y: 28, label: 'Grapes' }, + { y: 34, label: 'Lychee' }, + { y: 14, label: 'Jackfruit' } + ] + }] + }); + + chart.render(); + } + +} + diff --git a/frontend-syscop/src/app/chart-three/chart-three.component.css b/frontend-syscop/src/app/chart-three/chart-three.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend-syscop/src/app/chart-three/chart-three.component.html b/frontend-syscop/src/app/chart-three/chart-three.component.html new file mode 100644 index 0000000..8929f30 --- /dev/null +++ b/frontend-syscop/src/app/chart-three/chart-three.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/frontend-syscop/src/app/chart-three/chart-three.component.spec.ts b/frontend-syscop/src/app/chart-three/chart-three.component.spec.ts new file mode 100644 index 0000000..bad2087 --- /dev/null +++ b/frontend-syscop/src/app/chart-three/chart-three.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChartThreeComponent } from './chart-three.component'; + +describe('ChartThreeComponent', () => { + let component: ChartThreeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChartThreeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartThreeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/chart-three/chart-three.component.ts b/frontend-syscop/src/app/chart-three/chart-three.component.ts new file mode 100644 index 0000000..e5d5245 --- /dev/null +++ b/frontend-syscop/src/app/chart-three/chart-three.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit } from '@angular/core'; +import * as $ from 'jquery'; +import * as CanvasJS from 'src/canvasjs-2.3.1/canvasjs.min.js'; + +@Component({ + selector: 'app-chart-three', + templateUrl: './chart-three.component.html', + styleUrls: ['./chart-three.component.css'] +}) +export class ChartThreeComponent implements OnInit { + + constructor() { } + + ngOnInit() { + const dataPoints = []; + let dpsLength = 0; + const chart = new CanvasJS.Chart('chartContainer', { + theme: 'light1', + exportEnabled: true, + title: { + text: 'Live Chart with Data-Points from External JSON' + }, + data: [{ + type: 'spline', + dataPoints: dataPoints, + }] + }); + + $.getJSON('https://canvasjs.com/services/data/datapoints.php?xstart=1&ystart=25&length=20&type=json&callback=?', function (data) { + $.each(data, function (key, value) { + dataPoints.push({ x: value[0], y: parseInt(value[1], 10) }); + }); + dpsLength = dataPoints.length; + chart.render(); + updateChart(); + }); + function updateChart() { + $.getJSON('https://canvasjs.com/services/data/datapoints.php?xstart=' + (dpsLength + 1) + '&ystart=' + + (dataPoints[dataPoints.length - 1].y) + '&length=1&type=json&callback=?', function (data) { + $.each(data, function (key, value) { + dataPoints.push({ + x: parseInt(value[0], 10), + y: parseInt(value[1], 10) + }); + dpsLength++; + }); + + if (dataPoints.length > 20) { + dataPoints.shift(); + } + chart.render(); + setTimeout(function () { updateChart(); } , 1000); + }); + } + + } +} diff --git a/frontend-syscop/src/app/chart-two/chart-two.component.css b/frontend-syscop/src/app/chart-two/chart-two.component.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend-syscop/src/app/chart-two/chart-two.component.html b/frontend-syscop/src/app/chart-two/chart-two.component.html new file mode 100644 index 0000000..676f38f --- /dev/null +++ b/frontend-syscop/src/app/chart-two/chart-two.component.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/frontend-syscop/src/app/chart-two/chart-two.component.spec.ts b/frontend-syscop/src/app/chart-two/chart-two.component.spec.ts new file mode 100644 index 0000000..52d790f --- /dev/null +++ b/frontend-syscop/src/app/chart-two/chart-two.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChartTwoComponent } from './chart-two.component'; + +describe('ChartTwoComponent', () => { + let component: ChartTwoComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ChartTwoComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartTwoComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend-syscop/src/app/chart-two/chart-two.component.ts b/frontend-syscop/src/app/chart-two/chart-two.component.ts new file mode 100644 index 0000000..af6ef7d --- /dev/null +++ b/frontend-syscop/src/app/chart-two/chart-two.component.ts @@ -0,0 +1,41 @@ +import { Component, OnInit } from '@angular/core'; +import * as CanvasJS from 'src/canvasjs-2.3.1/canvasjs.min.js'; + +@Component({ + selector: 'app-chart-two', + templateUrl: './chart-two.component.html', + styleUrls: ['./chart-two.component.css'] +}) +export class ChartTwoComponent implements OnInit { + + constructor() { } + + ngOnInit() { + const chart = new CanvasJS.Chart('chartContainer2', { + theme: 'light2', + animationEnabled: true, + exportEnabled: true, + title: { + text: 'Monthly Expense' + }, + data: [{ + type: 'pie', + showInLegend: true, + toolTipContent: '{name}: ${y} (#percent%)', + indexLabel: '{name} - #percent%', + dataPoints: [ + { y: 450, name: 'Food' }, + { y: 120, name: 'Insurance' }, + { y: 300, name: 'Traveling' }, + { y: 800, name: 'Housing' }, + { y: 150, name: 'Education' }, + { y: 150, name: 'Shopping' }, + { y: 250, name: 'Others' } + ] + }] + }); + + chart.render(); + } + +} diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.css b/frontend-syscop/src/app/dashboard/dashboard.component.css index e69de29..49a8146 100644 --- a/frontend-syscop/src/app/dashboard/dashboard.component.css +++ b/frontend-syscop/src/app/dashboard/dashboard.component.css @@ -0,0 +1,21 @@ +.grid-container { + margin: 20px; +} + +.dashboard-card { + position: absolute; + top: 15px; + left: 15px; + right: 15px; + bottom: 15px; +} + +.more-button { + position: absolute; + top: 5px; + right: 10px; +} + +.dashboard-card-content { + text-align: center; +} diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.html b/frontend-syscop/src/app/dashboard/dashboard.component.html index fa60057..7e16ae2 100644 --- a/frontend-syscop/src/app/dashboard/dashboard.component.html +++ b/frontend-syscop/src/app/dashboard/dashboard.component.html @@ -1,3 +1,26 @@ - - Click anywhere - \ No newline at end of file +
+

Dashboard

+ + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts b/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts index 9c996c3..93f8d9f 100644 --- a/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts +++ b/frontend-syscop/src/app/dashboard/dashboard.component.spec.ts @@ -1,4 +1,13 @@ +import { LayoutModule } from '@angular/cdk/layout'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { + MatButtonModule, + MatCardModule, + MatGridListModule, + MatIconModule, + MatMenuModule, +} from '@angular/material'; import { DashboardComponent } from './dashboard.component'; @@ -8,9 +17,17 @@ describe('DashboardComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ DashboardComponent ] - }) - .compileComponents(); + declarations: [DashboardComponent], + imports: [ + NoopAnimationsModule, + LayoutModule, + MatButtonModule, + MatCardModule, + MatGridListModule, + MatIconModule, + MatMenuModule, + ] + }).compileComponents(); })); beforeEach(() => { @@ -19,7 +36,7 @@ describe('DashboardComponent', () => { fixture.detectChanges(); }); - it('should create', () => { + it('should compile', () => { expect(component).toBeTruthy(); }); }); diff --git a/frontend-syscop/src/app/dashboard/dashboard.component.ts b/frontend-syscop/src/app/dashboard/dashboard.component.ts index 5653abc..d7bba35 100644 --- a/frontend-syscop/src/app/dashboard/dashboard.component.ts +++ b/frontend-syscop/src/app/dashboard/dashboard.component.ts @@ -1,33 +1,36 @@ -import { Component, AfterContentInit } from '@angular/core'; -import * as d3 from 'd3'; +import { Component, OnInit } from '@angular/core'; +import { map } from 'rxjs/operators'; +import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout'; +import * as CanvasJS from 'src/canvasjs-2.3.1/canvasjs.min.js'; + + @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.css'] }) -export class DashboardComponent implements AfterContentInit { - - title = 'app'; +export class DashboardComponent implements OnInit { + /** Based on the screen size, switch from standard to one column per row */ + cards = this.breakpointObserver.observe(Breakpoints.Handset).pipe( + map(({ matches }) => { + if (matches) { + return [ + { title: 'Card 1', cols: 2, rows: 1 }, + { title: 'Card 2', cols: 2, rows: 1 }, + { title: 'Card 3', cols: 2, rows: 1 }, + { title: 'Card 4', cols: 2, rows: 1 } + ]; + } + return [ + { title: 'Card 1', cols: 2, rows: 1 }, + { title: 'Card 2', cols: 2, rows: 1 }, + { title: 'Card 3', cols: 2, rows: 1 }, + { title: 'Card 4', cols: 2, rows: 1 } + ]; + }) + ); - radius = 10; - - ngAfterContentInit() { - d3.select('p').style('color', 'red'); + constructor(private breakpointObserver: BreakpointObserver) { } + ngOnInit() {} } - - colorMe() { - d3.select('button').style('color', 'red'); - } - - clicked(event: any) { - d3.select(event.target).append('circle') - .attr('cx', event.x) - .attr('cy', event.y) - .attr('r', () => { - return this.radius; - }) - .attr('fill', 'red'); - } - -} diff --git a/frontend-syscop/src/app/home/home.component.css b/frontend-syscop/src/app/home/home.component.css index a765961..fefc781 100644 --- a/frontend-syscop/src/app/home/home.component.css +++ b/frontend-syscop/src/app/home/home.component.css @@ -7,12 +7,12 @@ } .sidenav .mat-toolbar { - background: rgba(20,30,45,1); + background: inherit; color: white; } .mat-toolbar.mat-primary { - position: sticky; + position: fixed; top: 0; z-index: 1000; background: rgba(20,30,45,1); diff --git a/frontend-syscop/src/app/home/home.component.html b/frontend-syscop/src/app/home/home.component.html index 4a8f695..f22a881 100644 --- a/frontend-syscop/src/app/home/home.component.html +++ b/frontend-syscop/src/app/home/home.component.html @@ -1,8 +1,8 @@ - + SYSCOP @@ -31,7 +31,12 @@ - SYSCOP @@ -42,5 +47,6 @@ + diff --git a/frontend-syscop/src/app/home/home.component.ts b/frontend-syscop/src/app/home/home.component.ts index c6cb1b9..d863408 100644 --- a/frontend-syscop/src/app/home/home.component.ts +++ b/frontend-syscop/src/app/home/home.component.ts @@ -2,8 +2,6 @@ import { Component } from '@angular/core'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { AppRoutingModule } from '../app-routing.module'; - @Component({ selector: 'app-home', templateUrl: './home.component.html', diff --git a/frontend-syscop/src/canvasjs-2.3.1/canvasjs.min.js b/frontend-syscop/src/canvasjs-2.3.1/canvasjs.min.js new file mode 100644 index 0000000..0ef7745 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/canvasjs.min.js @@ -0,0 +1,911 @@ +/* + CanvasJS HTML5 & JavaScript Charts - v2.3.1 GA - https://canvasjs.com/ + Copyright 2018 fenopix + + --------------------- License Information -------------------- + CanvasJS is a commercial product which requires purchase of license. Without a commercial license you can use it for evaluation purposes for upto 30 days. Please refer to the following link for further details. + https://canvasjs.com/license/ + +*/ +/*eslint-disable*/ +/*jshint ignore:start*/ +(function(){function qa(k,p){k.prototype=eb(p.prototype);k.prototype.constructor=k;k.base=p.prototype}function eb(k){function p(){}p.prototype=k;return new p}function Ya(k,p,D){"millisecond"===D?k.setMilliseconds(k.getMilliseconds()+1*p):"second"===D?k.setSeconds(k.getSeconds()+1*p):"minute"===D?k.setMinutes(k.getMinutes()+1*p):"hour"===D?k.setHours(k.getHours()+1*p):"day"===D?k.setDate(k.getDate()+1*p):"week"===D?k.setDate(k.getDate()+7*p):"month"===D?k.setMonth(k.getMonth()+1*p):"year"===D&&k.setFullYear(k.getFullYear()+ +1*p);return k}function $(k,p){var D=!1;0>k&&(D=!0,k*=-1);k=""+k;for(p=p?p:1;k.length
Please right click on the image and save it to your device
"), +p.document.close()}}}function N(k){var p=((k&16711680)>>16).toString(16),D=((k&65280)>>8).toString(16);k=((k&255)>>0).toString(16);p=2>p.length?"0"+p:p;D=2>D.length?"0"+D:D;k=2>k.length?"0"+k:k;return"#"+p+D+k}function fb(k,p){var D=this.length>>>0,r=Number(p)||0,r=0>r?Math.ceil(r):Math.floor(r);for(0>r&&(r+=D);rD;D++)if(k[D]!==k[D+4]|k[D]!==k[D+8]|k[D]!==k[D+12]){p=!1;break}return p?k[0]<<16|k[1]<<8|k[2]:0}function na(k,p,D){return k in p?p[k]:D[k]}function Oa(k,p,D){if(r&&bb){var u=k.getContext("2d");Pa=u.webkitBackingStorePixelRatio|| +u.mozBackingStorePixelRatio||u.msBackingStorePixelRatio||u.oBackingStorePixelRatio||u.backingStorePixelRatio||1;W=Ua/Pa;k.width=p*W;k.height=D*W;Ua!==Pa&&(k.style.width=p+"px",k.style.height=D+"px",u.scale(W,W))}else k.width=p,k.height=D}function hb(k){if(!ib){var p=!1,D=!1;"undefined"===typeof ra.Chart.creditHref?(k.creditHref=ja("iuuqr;..b`ow`rkr/bnl."),k.creditText=ja("B`ow`rKR/bnl")):(p=k.updateOption("creditText"),D=k.updateOption("creditHref"));if(k.creditHref&&k.creditText){k._creditLink|| +(k._creditLink=document.createElement("a"),k._creditLink.setAttribute("class","canvasjs-chart-credit"),k._creditLink.setAttribute("title","JavaScript Charts"),k._creditLink.setAttribute("style","outline:none;margin:0px;position:absolute;right:2px;top:"+(k.height-14)+"px;color:dimgrey;text-decoration:none;font-size:11px;font-family: Calibri, Lucida Grande, Lucida Sans Unicode, Arial, sans-serif"),k._creditLink.setAttribute("tabIndex",-1),k._creditLink.setAttribute("target","_blank"));if(0===k.renderCount|| +p||D)k._creditLink.setAttribute("href",k.creditHref),k._creditLink.innerHTML=k.creditText;k._creditLink&&k.creditHref&&k.creditText?(k._creditLink.parentElement||k._canvasJSContainer.appendChild(k._creditLink),k._creditLink.style.top=k.height-14+"px"):k._creditLink.parentElement&&k._canvasJSContainer.removeChild(k._creditLink)}}}function ta(k,p){Ja&&(this.canvasCount|=0,window.console.log(++this.canvasCount));var D=document.createElement("canvas");D.setAttribute("class","canvasjs-chart-canvas");Oa(D, +k,p);r||"undefined"===typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(D);return D}function sa(k,p,D){for(var r in D)p.style[r]=D[r]}function ua(k,p,D){p.getAttribute("state")||(p.style.backgroundColor=k.toolbar.backgroundColor,p.style.color=k.toolbar.fontColor,p.style.border="none",sa(k,p,{WebkitUserSelect:"none",MozUserSelect:"none",msUserSelect:"none",userSelect:"none"}));p.getAttribute("state")!==D&&(p.setAttribute("state",D),p.setAttribute("type","button"),sa(k,p,{padding:"5px 12px", +cursor:"pointer","float":"left",width:"40px",height:"25px",outline:"0px",verticalAlign:"baseline",lineHeight:"0"}),p.setAttribute("title",k._cultureInfo[D+"Text"]),p.innerHTML=""+k._cultureInfo[D+"Text"]+"")}function Qa(){for(var k=null,p=0;pa?"a":"p";case "tt":return 12>a?"am":"pm";case "T":return 12>a?"A": +"P";case "TT":return 12>a?"AM":"PM";case "K":return S?"UTC":(String(z).match(H)||[""]).pop().replace(F,"");case "z":return(0k?!0:!1;u&&(k*=-1);var v=r?r.decimalSeparator:".",H=r?r.digitGroupSeparator: +",",F="";p=String(p);var F=1,z=r="",E=-1,L=[],R=[],I=0,N=0,S=0,O=!1,U=0,z=p.match(/"[^"]*"|'[^']*'|[eE][+-]*[0]+|[,]+[.]|\u2030|./g);p=null;for(var Q=0;z&&QE)E=Q;else{if("%"===p)F*=100;else if("\u2030"===p){F*=1E3;continue}else if(","===p[0]&&"."===p[p.length-1]){F/=Math.pow(1E3,p.length-1);E=Q+p.length-1;continue}else"E"!==p[0]&&"e"!==p[0]||"0"!==p[p.length-1]||(O=!0);0>E?(L.push(p),"#"===p||"0"===p?I++:","===p&&S++):(R.push(p),"#"!==p&&"0"!==p||N++)}O&&(p=Math.floor(k), +z=-Math.floor(Math.log(k)/Math.LN10+1),U=0===k?0:0===p?-(I+z):String(p).length-I,F/=Math.pow(10,U));0>E&&(E=Q);F=(k*F).toFixed(N);p=F.split(".");F=(p[0]+"").split("");k=(p[1]+"").split("");F&&"0"===F[0]&&F.shift();for(O=z=Q=N=E=0;0U?p.replace("+","").replace("-",""):p.replace("-",""),r+=p.replace(/[0]+/,function(k){return $(U,k.length)}));H="";for(L=!1;0U?p.replace("+","").replace("-",""):p.replace("-",""),H+=p.replace(/[0]+/,function(k){return $(U,k.length)}));r+=(L?v:"")+H;return u?"-"+r:r},Ra=function(k){var p=0,r=0;k=k||window.event;k.offsetX||0===k.offsetX?(p=k.offsetX,r=k.offsetY):k.layerX||0==k.layerX?(p=k.layerX,r=k.layerY):(p=k.pageX-k.target.offsetLeft, +r=k.pageY-k.target.offsetTop);return{x:p,y:r}},bb=!0,Ua=window.devicePixelRatio||1,Pa=1,W=bb?Ua/Pa:1,ea=function(k,p,r,u,v,H,F,z,E,L,R,N,O){"undefined"===typeof O&&(O=1);F=F||0;z=z||"black";var I=15p)v=H-1;else break}r>p&&1H&&(F=p.pop(),u-=F.height,v=z)}this._wrappedText={lines:p,width:v,height:u};this.width=v+(this.leftPadding+this.rightPadding);this.height=u+(this.topPadding+this.bottomPadding);this.ctx.font=r};ka.prototype._getFontString=function(){var k;k=""+(this.fontStyle?this.fontStyle+" ":"");k+=this.fontWeight?this.fontWeight+" ":"";k+=this.fontSize?this.fontSize+"px ":"";var p=this.fontFamily?this.fontFamily+"":"";!r&&p&&(p=p.split(",")[0],"'"!==p[0]&&'"'!==p[0]&&(p="'"+p+"'"));return k+=p}; +qa(Va,V);qa(Aa,V);Aa.prototype.setLayout=function(){if(this.text){var k=this.dockInsidePlotArea?this.chart.plotArea:this.chart,p=k.layoutManager.getFreeSpace(),r=p.x1,v=p.y1,E=0,H=0,F=this.chart._menuButton&&this.chart.exportEnabled&&"top"===this.verticalAlign?22:0,z,I;"top"===this.verticalAlign||"bottom"===this.verticalAlign?(null===this.maxWidth&&(this.maxWidth=p.width-4-F*("center"===this.horizontalAlign?2:1)),H=0.5*p.height-this.margin-2,E=0):"center"===this.verticalAlign&&("left"===this.horizontalAlign|| +"right"===this.horizontalAlign?(null===this.maxWidth&&(this.maxWidth=p.height-4),H=0.5*p.width-this.margin-2):"center"===this.horizontalAlign&&(null===this.maxWidth&&(this.maxWidth=p.width-4),H=0.5*p.height-4));var L;u(this.padding)||"number"!==typeof this.padding?u(this.padding)||"object"!==typeof this.padding||(L=this.padding.top?this.padding.top:this.padding.bottom?this.padding.bottom:0,L+=this.padding.bottom?this.padding.bottom:this.padding.top?this.padding.top:0,L*=1.25):L=2.5*this.padding;this.wrap|| +(H=Math.min(H,Math.max(1.5*this.fontSize,this.fontSize+L)));H=new ka(this.ctx,{fontSize:this.fontSize,fontFamily:this.fontFamily,fontColor:this.fontColor,fontStyle:this.fontStyle,fontWeight:this.fontWeight,horizontalAlign:this.horizontalAlign,verticalAlign:this.verticalAlign,borderColor:this.borderColor,borderThickness:this.borderThickness,backgroundColor:this.backgroundColor,maxWidth:this.maxWidth,maxHeight:H,cornerRadius:this.cornerRadius,text:this.text,padding:this.padding,textBaseline:"top"}); +L=H.measureText();"top"===this.verticalAlign||"bottom"===this.verticalAlign?("top"===this.verticalAlign?(v=p.y1+2,I="top"):"bottom"===this.verticalAlign&&(v=p.y2-2-L.height,I="bottom"),"left"===this.horizontalAlign?r=p.x1+2:"center"===this.horizontalAlign?r=p.x1+p.width/2-L.width/2:"right"===this.horizontalAlign&&(r=p.x2-2-L.width-F),z=this.horizontalAlign,this.width=L.width,this.height=L.height):"center"===this.verticalAlign&&("left"===this.horizontalAlign?(r=p.x1+2,v=p.y2-2-(this.maxWidth/2-L.width/ +2),E=-90,I="left",this.width=L.height,this.height=L.width):"right"===this.horizontalAlign?(r=p.x2-2,v=p.y1+2+(this.maxWidth/2-L.width/2),E=90,I="right",this.width=L.height,this.height=L.width):"center"===this.horizontalAlign&&(v=k.y1+(k.height/2-L.height/2),r=k.x1+(k.width/2-L.width/2),I="center",this.width=L.width,this.height=L.height),z="center");H.x=r;H.y=v;H.angle=E;H.horizontalAlign=z;this._textBlock=H;k.layoutManager.registerSpace(I,{width:this.width+("left"===I||"right"===I?this.margin+2:0), +height:this.height+("top"===I||"bottom"===I?this.margin+2:0)});this.bounds={x1:r,y1:v,x2:r+this.width,y2:v+this.height};this.ctx.textBaseline="top"}};Aa.prototype.render=function(){this._textBlock&&this._textBlock.render(!0)};qa(Ka,V);Ka.prototype.setLayout=Aa.prototype.setLayout;Ka.prototype.render=Aa.prototype.render;Wa.prototype.get=function(k,p){var r=null;0a[g].x&&0w?{x:a[l].x+w/3,y:a[l].y+c/3}:{x:a[l].x,y:a[l].y+c/9};l=e;g=0===l?0:l-1;m=l===a.length-1?l:l+1;c=Math.abs((a[m].x-a[g].x)/(0===a[l].x-a[g].x?0.01:a[l].x-a[g].x))*(d- +1)/2+1;w=(a[m].x-a[g].x)/c;c=(a[m].y-a[g].y)/c;b[b.length]=a[l].x>a[g].x&&0w?{x:a[l].x-w/3,y:a[l].y-c/3}:{x:a[l].x,y:a[l].y-c/9};b[b.length]=a[e]}return b}function E(a,d,b,c,e,g,m,l,w,h){var s=0;h?(m.color=g,l.color=g):h=1;s=w?Math.abs(e-b):Math.abs(c-d);s=0this.labelAngle?this.labelAngle-=180:270<=this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360);this.options.scaleBreaks&&(this.scaleBreaks=new Q(this.chart, +this.options.scaleBreaks,++this.chart._eventManager.lastObjectId,this));this.stripLines=[];if(this.options.stripLines&&0=this._appliedBreaks[a+1].startValue&&(this._appliedBreaks[a].endValue=Math.max(this._appliedBreaks[a].endValue,this._appliedBreaks[a+1].endValue),window.console&&window.console.log("CanvasJS Error: Breaks "+a+" and "+(a+1)+" are overlapping."),this._appliedBreaks.splice(a,2),a--)}}function L(a,d,b,c,e,g){L.base.constructor.call(this,"Break",d,b,c,g);this.id=e;this.chart=a;this.ctx=this.chart.ctx;this.scaleBreaks=g;this.optionsName= +d;this.isOptionsInArray=!0;this.type=b.type?this.type:g.type;this.fillOpacity=u(b.fillOpacity)?g.fillOpacity:this.fillOpacity;this.lineThickness=u(b.lineThickness)?g.lineThickness:this.lineThickness;this.color=b.color?this.color:g.color;this.lineColor=b.lineColor?this.lineColor:g.lineColor;this.lineDashType=b.lineDashType?this.lineDashType:g.lineDashType;!u(this.startValue)&&this.startValue.getTime&&(this.startValue=this.startValue.getTime());!u(this.endValue)&&this.endValue.getTime&&(this.endValue= +this.endValue.getTime());"number"===typeof this.startValue&&("number"===typeof this.endValue&&this.endValue=navigator.userAgent.search("MSIE")&&sa(a,a._zoomButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers);O(this._zoomButton,"mouseout",function(){d||(sa(a,a._zoomButton,{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._zoomButton.childNodes[0],{WebkitFilter:"invert(0%)", +filter:"invert(0%)"}))},this.allDOMEventHandlers)}this._resetButton||(d=!1,va(this._resetButton=document.createElement("button")),ua(this,this._resetButton,"reset"),this._resetButton.style.borderRight=(this.exportEnabled?this.toolbar.borderThickness:0)+"px solid "+this.toolbar.borderColor,this._toolBar.appendChild(this._resetButton),O(this._resetButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers),O(this._resetButton,"click",function(){a.toolTip.hide();a.zoomEnabled||a.panEnabled?(a.zoomEnabled= +!0,a.panEnabled=!1,ua(a,a._zoomButton,"pan"),a._defaultCursor="default",a.overlaidCanvas.style.cursor=a._defaultCursor):(a.zoomEnabled=!1,a.panEnabled=!1);if(a.sessionVariables.axisX)for(var c=0;c=navigator.userAgent.search("MSIE")&&sa(a,a._resetButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers),O(this._resetButton,"mouseout",function(){d||(sa(a,a._resetButton, +{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._resetButton.childNodes[0],{WebkitFilter:"invert(0%)",filter:"invert(0%)"}))},this.allDOMEventHandlers),this.overlaidCanvas.style.cursor=a._defaultCursor);this.zoomEnabled||this.panEnabled||(this._zoomButton?(a._zoomButton.getAttribute("state")===a._cultureInfo.zoomText?(this.panEnabled=!0,this.zoomEnabled=!1):(this.zoomEnabled=!0,this.panEnabled= +!1),Qa(a._zoomButton,a._resetButton)):(this.zoomEnabled=!0,this.panEnabled=!1))}else this.panEnabled=this.zoomEnabled=!1;this._menuButton?this.exportEnabled?Qa(this._menuButton):va(this._menuButton):this.exportEnabled&&r&&(d=!1,this._menuButton=document.createElement("button"),ua(this,this._menuButton,"menu"),this._toolBar.appendChild(this._menuButton),O(this._menuButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers),O(this._menuButton,"click",function(){"none"!==a._dropdownMenu.style.display|| +a._dropDownCloseTime&&500>=(new Date).getTime()-a._dropDownCloseTime.getTime()||(a._dropdownMenu.style.display="block",a._menuButton.blur(),a._dropdownMenu.focus())},this.allDOMEventHandlers,!0),O(this._menuButton,"mouseover",function(){d||(sa(a,a._menuButton,{backgroundColor:a.toolbar.backgroundColorOnHover,color:a.toolbar.fontColorOnHover}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._menuButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers,!0), +O(this._menuButton,"mouseout",function(){d||(sa(a,a._menuButton,{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._menuButton.childNodes[0],{WebkitFilter:"invert(0%)",filter:"invert(0%)"}))},this.allDOMEventHandlers,!0));if(!this._dropdownMenu&&this.exportEnabled&&r){d=!1;this._dropdownMenu=document.createElement("div");this._dropdownMenu.setAttribute("tabindex",-1);var b=-1!==this.theme.indexOf("dark")?"black":"#888888";this._dropdownMenu.style.cssText= +"position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 0px;top: 25px;min-width: 120px;outline: 0;font-size: 14px; font-family: Arial, Helvetica, sans-serif;padding: 5px 0px 5px 0px;text-align: left;line-height: 10px;background-color:"+this.toolbar.backgroundColor+";box-shadow: 2px 2px 10px "+b;a._dropdownMenu.style.display="none";this._toolBar.appendChild(this._dropdownMenu);O(this._dropdownMenu,"blur",function(){va(a._dropdownMenu); +a._dropDownCloseTime=new Date},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText="padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.printText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers, +!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){a.print();va(a._dropdownMenu)},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText="padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.saveJPGText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d= +!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){Ta(a.canvas,"jpeg",a.exportFileName);va(a._dropdownMenu)},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText= +"padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.savePNGText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor, +this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){Ta(a.canvas,"png",a.exportFileName);va(a._dropdownMenu)},this.allDOMEventHandlers,!0)}"none"!==this._toolBar.style.display&&this._zoomButton&&(this.panEnabled?ua(a,a._zoomButton,"zoom"):ua(a,a._zoomButton,"pan"),a._resetButton.getAttribute("state")!==a._cultureInfo.resetText&&ua(a,a._resetButton,"reset"));this.options.toolTip&&this.toolTip.options!==this.options.toolTip&&(this.toolTip.options=this.options.toolTip); +for(var c in this.toolTip.options)this.toolTip.options.hasOwnProperty(c)&&this.toolTip.updateOption(c)};p.prototype._updateSize=function(){var a;a=[this.canvas,this._preRenderCanvas,this.overlaidCanvas,this._eventManager.ghostCanvas];var d=0,b=0;this.options.width?d=this.width:this.width=d=0c.linkedDataSeriesIndex||c.linkedDataSeriesIndex>=this.options.data.length||"number"!==typeof c.linkedDataSeriesIndex|| +"error"===this.options.data[c.linkedDataSeriesIndex].type)&&(c.linkedDataSeriesIndex=null);null===c.name&&(c.name="DataSeries "+a);null===c.color?1a&&"undefined"!==typeof w.startTimePercent?a>=w.startTimePercent&&w.animationCallback(w.easingFunction(a-w.startTimePercent,0,1,1-w.startTimePercent),w):w.animationCallback(w.easingFunction(a,0,1,1),w);s.dispatchEvent("dataAnimationIterationEnd",{chart:s})},function(){b=[];for(var a=0;aa.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=0;mb.max&&(b.max=c);ed.max&&"number"===typeof e&&(d.max=e);if(0B&&(B=1/B);b.minDiff>B&&1!==B&&(b.minDiff=B)}else B=c-l.dataPoints[w-1].x,0>B&&(B*=-1),b.minDiff>B&&0!==B&&(b.minDiff=B);null!==e&&null!==l.dataPoints[w-1].y&&(a.axisY.logarithmic?(B=e/l.dataPoints[w-1].y,1>B&&(B=1/B),d.minDiff>B&&1!==B&&(d.minDiff=B)):(B=e-l.dataPoints[w-1].y,0>B&&(B*=-1),d.minDiff>B&&0!==B&&(d.minDiff=B)))}if(cf&& +!s)s=!0;else if(c>f&&s)continue;l.dataPoints[w].label&&(a.axisX.labels[c]=l.dataPoints[w].label);cb.viewPortMax&&(b.viewPortMax=c);null===e?b.viewPortMin===c&&qd.viewPortMax&&"number"===typeof e&&(d.viewPortMax=e))}}l.axisX.valueType=l.xValueType=g?"dateTime":"number"}};p.prototype._processStackedPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var d= +a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=[],l=[],w=Infinity,h=-Infinity,s=0;sb.max&&(b.max=c);if(0r&&(r=1/r);b.minDiff>r&&1!==r&&(b.minDiff=r)}else r=c-q.dataPoints[n-1].x,0>r&&(r*=-1),b.minDiff>r&&0!==r&&(b.minDiff=r);null!==e&&null!==q.dataPoints[n-1].y&&(a.axisY.logarithmic?0r&&(r=1/r),d.minDiff>r&&1!==r&&(d.minDiff=r)):(r=e-q.dataPoints[n-1].y,0>r&&(r*=-1),d.minDiff>r&&0!==r&&(d.minDiff=r)))}if(ct&&!B)B=!0;else if(c>t&&B)continue;q.dataPoints[n].label&&(a.axisX.labels[c]=q.dataPoints[n].label);cb.viewPortMax&&(b.viewPortMax=c);null===q.dataPoints[n].y?b.viewPortMin===c&&kd.max&&(d.max=a),nb.viewPortMax||(ad.viewPortMax&& +(d.viewPortMax=a)));for(n in l)l.hasOwnProperty(n)&&!isNaN(n)&&(a=l[n],ad.max&&(d.max=Math.max(a,h)),nb.viewPortMax||(ad.viewPortMax&&(d.viewPortMax=Math.max(a,h))))}};p.prototype._processStacked100PlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=!1,l=!1,w=[],h=0;hb.max&&(b.max=c);if(0t&&(t=1/t);b.minDiff>t&&1!==t&&(b.minDiff=t)}else t=c-s.dataPoints[q-1].x,0>t&&(t*=-1),b.minDiff>t&&0!==t&&(b.minDiff=t);u(e)||null===s.dataPoints[q-1].y||(a.axisY.logarithmic?0t&&(t=1/t),d.minDiff>t&&1!==t&&(d.minDiff=t)):(t=e-s.dataPoints[q-1].y,0>t&&(t*=-1),d.minDiff>t&&0!==t&&(d.minDiff=t)))}if(cr&&!f)f=!0;else if(c>r&&f)continue;s.dataPoints[q].label&&(a.axisX.labels[c]=s.dataPoints[q].label); +cb.viewPortMax&&(b.viewPortMax=c);null===e?b.viewPortMin===c&&Be&&(l=!0),w[c]=w[c]?w[c]+Math.abs(e):Math.abs(e))}}s.axisX.valueType=s.xValueType=g?"dateTime":"number"}a.axisY.logarithmic?(d.max=u(d.viewPortMax)?99*Math.pow(a.axisY.logarithmBase,-0.05):Math.max(d.viewPortMax,99*Math.pow(a.axisY.logarithmBase,-0.05)),d.min=u(d.viewPortMin)?1:Math.min(d.viewPortMin,1)):m&&!l?(d.max=u(d.viewPortMax)? +99:Math.max(d.viewPortMax,99),d.min=u(d.viewPortMin)?1:Math.min(d.viewPortMin,1)):m&&l?(d.max=u(d.viewPortMax)?99:Math.max(d.viewPortMax,99),d.min=u(d.viewPortMin)?-99:Math.min(d.viewPortMin,-99)):!m&&l&&(d.max=u(d.viewPortMax)?-1:Math.max(d.viewPortMax,-1),d.min=u(d.viewPortMin)?-99:Math.min(d.viewPortMin,-99));d.viewPortMin=d.min;d.viewPortMax=d.max;a.dataPointYSums=w}};p.prototype._processMultiYPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo, +b=a.axisX.dataInfo,c,e,g,m,l=!1,w=0;wb.max&&(b.max=c);gd.max&&(d.max=m);0B&&(B=1/B),b.minDiff>B&&1!==B&&(b.minDiff=B)):(B=c-h.dataPoints[s-1].x,0>B&&(B*=-1),b.minDiff>B&&0!==B&&(b.minDiff=B)),e&&(null!==e[0]&&h.dataPoints[s-1].y&&null!==h.dataPoints[s-1].y[0])&&(a.axisY.logarithmic?(B=e[0]/ +h.dataPoints[s-1].y[0],1>B&&(B=1/B),d.minDiff>B&&1!==B&&(d.minDiff=B)):(B=e[0]-h.dataPoints[s-1].y[0],0>B&&(B*=-1),d.minDiff>B&&0!==B&&(d.minDiff=B))));if(!(ct&&!n)n=!0;else if(c>t&&n)continue;h.dataPoints[s].label&&(a.axisX.labels[c]=h.dataPoints[s].label);cb.viewPortMax&&(b.viewPortMax=c);if(b.viewPortMin===c&&e)for(p=0;pd.viewPortMax&&(d.viewPortMax=m))}}h.axisX.valueType=h.xValueType=l?"dateTime":"number"}};p.prototype._processSpecificPlotUnit=function(a){if("waterfall"===a.type&&a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=0;mb.max&&(b.max=c),l.dataPointEOs[w].cumulativeSumd.max&&(d.max=l.dataPointEOs[w].cumulativeSum),0q&&(q=1/q),b.minDiff>q&&1!==q&&(b.minDiff=q)):(q=c-l.dataPoints[w-1].x,0>q&&(q*=-1),b.minDiff>q&&0!==q&&(b.minDiff=q)),null!==e&&null!==l.dataPoints[w- +1].y&&(a.axisY.logarithmic?(e=l.dataPointEOs[w].cumulativeSum/l.dataPointEOs[w-1].cumulativeSum,1>e&&(e=1/e),d.minDiff>e&&1!==e&&(d.minDiff=e)):(e=l.dataPointEOs[w].cumulativeSum-l.dataPointEOs[w-1].cumulativeSum,0>e&&(e*=-1),d.minDiff>e&&0!==e&&(d.minDiff=e)))),!(cf&&!s)s=!0;else if(c>f&&s)continue;l.dataPoints[w].label&&(a.axisX.labels[c]=l.dataPoints[w].label);cb.viewPortMax&&(b.viewPortMax=c);0d.viewPortMax&&(d.viewPortMax=l.dataPointEOs[w-1].cumulativeSum));l.dataPointEOs[w].cumulativeSumd.viewPortMax&&(d.viewPortMax=l.dataPointEOs[w].cumulativeSum)}l.axisX.valueType=l.xValueType=g?"dateTime":"number"}};p.prototype.calculateAutoBreaks=function(){function a(a,c,b,e){if(e)return b= +Math.pow(Math.min(b*a/c,c/a),0.2),1>=b&&(b=Math.pow(1>a?1/a:Math.min(c/a,a),0.25)),{startValue:a*b,endValue:c/b};b=0.2*Math.min(b-c+a,c-a);0>=b&&(b=0.25*Math.min(c-a,Math.abs(a)));return{startValue:a+b,endValue:c-b}}function d(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){var c=a.axisX.scaleBreaks&&a.axisX.scaleBreaks.autoCalculate&&1<=a.axisX.scaleBreaks.maxNumberOfAutoBreaks,b=a.axisY.scaleBreaks&&a.axisY.scaleBreaks.autoCalculate&&1<=a.axisY.scaleBreaks.maxNumberOfAutoBreaks;if(c|| +b)for(var d=a.axisY.dataInfo,f=a.axisX.dataInfo,g,h=f.min,l=f.max,m=d.min,n=d.max,f=f._dataRanges,d=d._dataRanges,q,w=0,s=0;sk.dataPoints.length))for(w=0;wf[q].max&&(f[q].max=g)),b){var r= +(n+1-m)*Math.max(parseFloat(a.axisY.scaleBreaks.collapsibleThreshold)||10,10)/100;if((g="waterfall"===a.type?k.dataPointEOs[w].cumulativeSum:k.dataPoints[w].y)&&g.length)for(var p=0;pd[q].max&&(d[q].max=g[p]);else u(g)||(q=Math.floor((g-m)/r),gd[q].max&&(d[q].max=g))}}}}function b(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)&&a.axisX.scaleBreaks&&a.axisX.scaleBreaks.autoCalculate&&1<= +a.axisX.scaleBreaks.maxNumberOfAutoBreaks)for(var c=a.axisX.dataInfo,b=c.min,d=c.max,f=c._dataRanges,g,h=0,l=0;lm.dataPoints.length))for(h=0;hf[g].max&&(f[g].max=c)}}for(var c,e=this,g=!1,m=0;m< +this._axes.length;m++)if(this._axes[m].scaleBreaks&&this._axes[m].scaleBreaks.autoCalculate&&1<=this._axes[m].scaleBreaks.maxNumberOfAutoBreaks){g=!0;this._axes[m].dataInfo._dataRanges=[];for(var l=0;l<100/Math.max(parseFloat(this._axes[m].scaleBreaks.collapsibleThreshold)||10,10);l++)this._axes[m].dataInfo._dataRanges.push({min:Infinity,max:-Infinity})}if(g){for(m=0;ms[f].max&&(s[f].max=q)}delete this._axes[m].dataInfo.dataPointYPositiveSums}if(this._axes[m].dataInfo.dataPointYNegativeSums){n= +this._axes[m].dataInfo.dataPointYNegativeSums;s=h;for(l in n)n.hasOwnProperty(l)&&!isNaN(l)&&(q=-1*n[l],u(q)||(f=Math.floor((q-w)/c),qs[f].max&&(s[f].max=q)));delete this._axes[m].dataInfo.dataPointYNegativeSums}for(l=0;lc&&g.push({diff:q,start:s,end:w});break}else l++;if(this._axes[m].scaleBreaks.customBreaks)for(l=0;l=e.x1&&(a<=e.x2&&d>=e.y1&&d<=e.y2)&&(c=e.id)}return c};p.prototype.getAutoFontSize=lb;p.prototype.resetOverlayedCanvas=function(){this.overlaidCanvasCtx.clearRect(0,0,this.width,this.height)};p.prototype.clearCanvas=kb;p.prototype.attachEvent=function(a){this._events.push(a)};p.prototype._touchEventHandler=function(a){if(a.changedTouches&&this.interactivityEnabled){var d=[],b=a.changedTouches,c=b?b[0]:a,e=null;switch(a.type){case "touchstart":case "MSPointerDown":d=["mousemove","mousedown"];this._lastTouchData= +Ra(c);this._lastTouchData.time=new Date;break;case "touchmove":case "MSPointerMove":d=["mousemove"];break;case "touchend":case "MSPointerUp":var g=this._lastTouchData&&this._lastTouchData.time?new Date-this._lastTouchData.time:0,d="touchstart"===this._lastTouchEventType||"MSPointerDown"===this._lastTouchEventType||300>g?["mouseup","click"]:["mouseup"];break;default:return}if(!(b&&1g)this._lastTouchData.scroll=!0}catch(l){}this._lastTouchEventType=a.type;if(this._lastTouchData.scroll&&this.zoomEnabled)this.isDrag&&this.resetOverlayedCanvas(),this.isDrag=!1;else for(b=0;b=e.x1&&d.x<=e.x2&&d.y>=e.y1&&d.y<=e.y2){c[b].call(c.context,d.x,d.y);"mousedown"===b&&!0===c.capture?(p.capturedEventParam=c,this.overlaidCanvas.setCapture?this.overlaidCanvas.setCapture():document.documentElement.addEventListener("mouseup", +this._mouseEventHandler,!1)):"mouseup"===b&&(c.chart.overlaidCanvas.releaseCapture?c.chart.overlaidCanvas.releaseCapture():document.documentElement.removeEventListener("mouseup",this._mouseEventHandler,!1));break}else c=null;a.target.style.cursor=c&&c.cursor?c.cursor:this._defaultCursor}b=this.plotArea;if(d.xb.x2||d.yb.y2)this.toolTip&&this.toolTip.enabled?this.toolTip.hide():this.resetOverlayedCanvas();this.isDrag&&this.zoomEnabled||!this._eventManager||this._eventManager.mouseEventHandler(a)}}; +p.prototype._plotAreaMouseDown=function(a,d){this.isDrag=!0;this.dragStartPoint={x:a,y:d}};p.prototype._plotAreaMouseUp=function(a,d){if(("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)&&this.isDrag){var b=d-this.dragStartPoint.y,c=a-this.dragStartPoint.x,e=0<=this.zoomType.indexOf("x"),g=0<=this.zoomType.indexOf("y"),m=!1;this.resetOverlayedCanvas();if("xySwapped"===this.plotInfo.axisPlacement)var l=g,g=e,e=l;if(this.panEnabled||this.zoomEnabled){if(this.panEnabled)for(e= +g=0;eb.maximum&&(g=b.viewportMaximum/b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum/g,b.sessionVariables.newViewportMaximum=b.viewportMaximum/g,m=!0):b.viewportMinimumb.maximum&&(g=b.viewportMaximum-b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum-g,b.sessionVariables.newViewportMaximum=b.viewportMaximum-g,m=!0);else if((!e||2Math.abs(b)&&(this.panEnabled||this.zoomEnabled)?this.toolTip.hide():this.panEnabled||this.zoomEnabled||this.toolTip.mouseMoveHandler(a, +d);if((!e||2f)var B=f,f=n,n=B;if(q.scaleBreaks)for(B=0;!g&&B=f;if(isFinite(q.dataInfo.minDiff))if(B=q.getApparentDifference(n,f,null,!0),!(g||!(this.panEnabled&&q.scaleBreaks&&q.scaleBreaks._appliedBreaks.length)&&(q.logarithmic&&Bq.maximum))w.push(q),s.push({val1:n,val2:f}),l=!0;else if(!e){l=!1;break}}return{isValid:l,axesWithValidRange:w,axesRanges:s}};p.prototype.preparePlotArea=function(){var a=this.plotArea;!r&&(0b.lineCoordinates.x2?d.x2:b.lineCoordinates.x2;a.y2=d.y2>d.y1?d.y2:b.lineCoordinates.y2;a.width=a.x2-a.x1;a.height=a.y2-a.y1}this.axisY2&&0b.lineCoordinates.x2?d.x2:b.lineCoordinates.x2,a.y2=d.y2>d.y1?d.y2:b.lineCoordinates.y2,a.width=a.x2-a.x1,a.height=a.y2-a.y1)}else d= +this.layoutManager.getFreeSpace(),a.x1=d.x1,a.x2=d.x2,a.y1=d.y1,a.y2=d.y2,a.width=d.width,a.height=d.height;r||(a.canvas.width=a.width,a.canvas.height=a.height,a.canvas.style.left=a.x1+"px",a.canvas.style.top=a.y1+"px",(0b.x2||h.point.yb.y2+1)continue}else if("rangearea"===s||"rangesplinearea"===s){if(h.dataPoint.xy.viewportMaximum||Math.max.apply(null,h.dataPoint.y)A.viewportMaximum)continue}else if(0<=s.indexOf("line")||0<=s.indexOf("area")||0<=s.indexOf("bubble")||0<=s.indexOf("scatter")){if(h.dataPoint.xy.viewportMaximum|| +h.dataPoint.yA.viewportMaximum)continue}else if(0<=s.indexOf("column")||"waterfall"===s||"error"===s&&!h.axisSwapped){if(h.dataPoint.xy.viewportMaximum||h.bounds.y1>b.y2||h.bounds.y2y.viewportMaximum||h.bounds.x1>b.x2||h.bounds.x2 +y.viewportMaximum||Math.max.apply(null,h.dataPoint.y)A.viewportMaximum)continue}else if(h.dataPoint.xy.viewportMaximum)continue;e=m=2;"horizontal"===C?(l=f.width,w=f.height):(w=f.width,l=f.height);if("normal"===this.plotInfo.axisPlacement){if(0<=s.indexOf("line")||0<=s.indexOf("area"))t="auto",m=4;else if(0<=s.indexOf("stacked"))"auto"===t&&(t="inside");else if("bubble"===s||"scatter"===s)t="inside";q=h.point.x- +l/2;"inside"!==t?(e=b.y1,g=b.y2,0h.point.y)):(n=h.point.y+m+c,n>g-w-m-c&&(n="auto"===t?Math.min(h.point.y,g)-w-m-c:g-w-m-c,v=ng-w-m&&("bubble"===s||"scatter"===s)&&(n=Math.min(h.point.y+m,b.y2-w-m))),n=Math.min(n,g-w))}else 0<=s.indexOf("line")||0<=s.indexOf("area")||0<=s.indexOf("scatter")?(t="auto",e=4):0<=s.indexOf("stacked")?"auto"===t&&(t="inside"):"bubble"===s&&(t="inside"),n=h.point.y-w/2,"inside"!==t?(m=b.x1,g=b.x2,0>ma?(q=h.point.x-l-e-c,qh.point.x)):(q=h.point.x+e+c,q>g-l-e-c&&(q="auto"=== +t?Math.min(h.point.x,g)-l-e-c:g-l-e-c,v=qma?Math.max(h.bounds.x1,b.x1)+l/2+e:Math.min(h.bounds.x2,b.x2)-l/2-e:(Math.max(h.bounds.x1,b.x1)+Math.min(h.bounds.x2,b.x2))/2,q=0>ma?Math.max(h.point.x,c)-l/2:Math.min(h.point.x,c)-l/2,q=Math.max(q,m));"vertical"===C&&(n+=w);f.x=q;f.y=n;f.render(!0);p&&("inside"!==t&&(0>s.indexOf("bar")&&("error"!==s||!h.axisSwapped)&&h.point.x>b.x1&&h.point.xs.indexOf("column")&&("error"!==s||h.axisSwapped)&&h.point.y>b.y1&&h.point.y=a.dataSeriesIndexes.length)){var c= +this._eventManager.ghostCtx;b.save();var e=this.plotArea;b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();for(var g=[],m,l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!u)))if("number"!==typeof s[t].y)0s[t].y===a.axisY.reversed?1:-1,color:B})}b.stroke();r&&c.stroke()}}ia.drawMarkers(g);r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&& +b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),c.beginPath());b.restore();b.beginPath();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStepLine=function(a){var d=a.targetCanvasCtx|| +this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx;b.save();var e=this.plotArea;b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();for(var g=[],m,l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!u)))if("number"!==typeof s[t].y)0s[t].y===a.axisY.reversed?1:-1,color:B})}b.stroke();r&&c.stroke()}}ia.drawMarkers(g);r&& +(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),c.beginPath());b.restore();b.beginPath();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation, +easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderSpline=function(a){function d(a){a=v(a,2);if(0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx;c.save();var g=this.plotArea;c.beginPath(); +c.rect(g.x1,g.y1,g.width,g.height);c.clip();for(var m=[],l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!u)))if("number"!==typeof s[p].y)0s[p].y===a.axisY.reversed?1:-1,color:B});u=!1}d(x)}ia.drawMarkers(m);r&&(b.drawImage(this._preRenderCanvas, +0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(g.x1,g.y1,g.width,g.height),e.beginPath());c.restore();c.beginPath();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear, +animationBase:0}}};p.prototype.renderColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,m,l,w,h=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width, +0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0,q=a.axisX.dataInfo.minDiff;isFinite(q)||(q=0.3*Math.abs(a.axisX.range));q=this.dataPointWidth=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(q)/Math.log(a.axisX.range):Math.abs(q)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>s&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(q=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(s=0;sa.axisX.dataInfo.viewPortMax)&&"number"===typeof B[g].y){m=a.axisX.convertValueToPixel(w);l=a.axisY.convertValueToPixel(B[g].y);m=a.axisX.reversed?m+a.plotType.totalDataSeries*q/2-(a.previousDataSeriesCount+s)*q<<0:m-a.plotType.totalDataSeries*q/2+(a.previousDataSeriesCount+s)*q<<0;var k=a.axisX.reversed?m-q<<0:m+q<<0,t;0<=B[g].y?t=h:(t=l,l=h);l>t&&(c=l,l=t,t=c);c=B[g].color?B[g].color:f._colorSet[g%f._colorSet.length];ea(b,m,l,k,t,c,0,null,p&&0<=B[g].y, +0>B[g].y&&p,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,x1:m,y1:l,x2:k,y2:t};c=N(c);r&&ea(this._eventManager.ghostCtx,m,l,k,t,c,0,null,!1,!1,!1,!1);(B[g].indexLabel||f.indexLabel||B[g].indexLabelFormatter||f.indexLabelFormatter)&&this._indexLabels.push({chartType:"column",dataPoint:B[g],dataSeries:f,point:{x:m+(k-m)/2,y:0>B[g].y===a.axisY.reversed?l:t},direction:0>B[g].y===a.axisY.reversed?1:-1,bounds:{x1:m, +y1:Math.min(l,t),x2:k,y2:Math.max(l,t)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore(); +return{source:d,dest:this.plotArea.ctx,animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:ha.axisY.bounds.y2?a.axisY.bounds.y2:h}}};p.prototype.renderStackedColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth? +this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.width<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>s&&(h=Math.min(this.options.dataPointWidth? +this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(f=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&&"number"===typeof t[h].y){s=a.axisX.convertValueToPixel(c);var x=s-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=t[h].y)w[c]=t[h].y+(w[c]?w[c]:0),y=a.axisY.convertValueToPixel(w[c]),q="undefined"!==typeof m[c]?m[c]:n,m[c]=y;else if(q=a.axisY.convertValueToPixel(t[h].y),0<=t[h].y){var A="undefined"!==typeof g[c]?g[c]:0;q-=A;y=n-A;g[c]=A+(y-q)}else A=m[c]?m[c]:0,y=q+A,q=n+A,m[c]=A+(y-q);c=t[h].color?t[h].color:p._colorSet[h%p._colorSet.length];ea(b,x,q,v,y,c,0,null,u&&0<=t[h].y,0>t[h].y&&u,!1, +!1,p.fillOpacity);c=p.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:k,dataPointIndex:h,x1:x,y1:q,x2:v,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,q,v,y,c,0,null,!1,!1,!1,!1);(t[h].indexLabel||p.indexLabel||t[h].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedColumn",dataPoint:t[h],dataSeries:p,point:{x:s,y:0<=t[h].y?q:y},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(q,y),x2:v,y2:Math.max(q, +y)},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx, +animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.y2?a.axisY.bounds.y2:n}}};p.prototype.renderStackedColumn100=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth?this.dataPointMinWidth: +this.options.dataPointWidth?this.dataPointWidth:1;s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.width<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>s&&(h=Math.min(this.options.dataPointWidth? +this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(f=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&&"number"===typeof t[h].y){s=a.axisX.convertValueToPixel(c);q=0!==a.dataPointYSums[c]?100*(t[h].y/a.dataPointYSums[c]):0;var x=s-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=l[c])continue;q=a.axisY.convertValueToPixel(l[c]);y=g[c]?g[c]:n;g[c]=q}else if(a.axisY.scaleBreaks&&0=t[h].y)w[c]=q+("undefined"!==typeof w[c]?w[c]:0),y=a.axisY.convertValueToPixel(w[c]),q=m[c]?m[c]:n,m[c]=y;else if(q=a.axisY.convertValueToPixel(q),0<=t[h].y){var A="undefined"!==typeof g[c]?g[c]:0;q-=A;y=n-A;a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.y1-q)&&(q=e.y1);g[c]=A+(y-q)}else A="undefined"!==typeof m[c]? +m[c]:0,y=q+A,q=n+A,a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.y2-y)&&(y=e.y2),m[c]=A+(y-q);c=t[h].color?t[h].color:k._colorSet[h%k._colorSet.length];ea(b,x,q,v,y,c,0,null,u&&0<=t[h].y,0>t[h].y&&u,!1,!1,k.fillOpacity);c=k.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:h,x1:x,y1:q,x2:v,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,q,v,y,c,0,null,!1,!1,!1,!1);(t[h].indexLabel||k.indexLabel||t[h].indexLabelFormatter||k.indexLabelFormatter)&& +this._indexLabels.push({chartType:"stackedColumn100",dataPoint:t[h],dataSeries:k,point:{x:s,y:0<=t[h].y?q:y},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(q,y),x2:v,y2:Math.max(q,y)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&& +this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.y2?a.axisY.bounds.y2:n}}};p.prototype.renderBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c= +null,e=this.plotArea,g=0,m,l,w,h=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0,q=a.axisX.dataInfo.minDiff;isFinite(q)||(q=0.3*Math.abs(a.axisX.range));q=this.options.dataPointWidth? +this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(q)/Math.log(a.axisX.range):Math.abs(q)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>s&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(q=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height); +b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(s=0;sa.axisX.dataInfo.viewPortMax)&&"number"===typeof B[g].y){l=a.axisX.convertValueToPixel(w); +m=a.axisY.convertValueToPixel(B[g].y);l=a.axisX.reversed?l+a.plotType.totalDataSeries*q/2-(a.previousDataSeriesCount+s)*q<<0:l-a.plotType.totalDataSeries*q/2+(a.previousDataSeriesCount+s)*q<<0;var p=a.axisX.reversed?l-q<<0:l+q<<0,t;0<=B[g].y?t=h:(t=m,m=h);c=B[g].color?B[g].color:f._colorSet[g%f._colorSet.length];ea(b,t,l,m,p,c,0,null,k,!1,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,x1:t,y1:l,x2:m,y2:p};c= +N(c);r&&ea(this._eventManager.ghostCtx,t,l,m,p,c,0,null,!1,!1,!1,!1);(B[g].indexLabel||f.indexLabel||B[g].indexLabelFormatter||f.indexLabelFormatter)&&this._indexLabels.push({chartType:"bar",dataPoint:B[g],dataSeries:f,point:{x:0<=B[g].y?m:t,y:l+(p-l)/2},direction:0>B[g].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(t,m),y1:l,x2:Math.max(t,m),y2:p},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas, +0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:ha.axisY.bounds.x2?a.axisY.bounds.x2: +h}}};p.prototype.renderStackedBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;q=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.height<< +0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>q&&(h=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,q));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&qq&&(f=q);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&&"number"===typeof t[h].y){q=a.axisX.convertValueToPixel(c);var x=q-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=t[h].y)w[c]=t[h].y+(w[c]?w[c]:0),s=m[c]? +m[c]:n,m[c]=y=a.axisY.convertValueToPixel(w[c]);else if(s=a.axisY.convertValueToPixel(t[h].y),0<=t[h].y){var A=g[c]?g[c]:0;y=n+A;s+=A;g[c]=A+(s-y)}else A=m[c]?m[c]:0,y=s-A,s=n-A,m[c]=A+(s-y);c=t[h].color?t[h].color:k._colorSet[h%k._colorSet.length];ea(b,y,x,s,v,c,0,null,u,!1,!1,!1,k.fillOpacity);c=k.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:h,x1:y,y1:x,x2:s,y2:v};c=N(c);r&&ea(this._eventManager.ghostCtx,y,x,s,v,c,0,null,!1,!1,!1, +!1);(t[h].indexLabel||k.indexLabel||t[h].indexLabelFormatter||k.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar",dataPoint:t[h],dataSeries:k,point:{x:0<=t[h].y?s:y,y:q},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(y,s),y1:x,x2:Math.max(y,s),y2:v},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&& +b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.x2?a.axisY.bounds.x2:n}}};p.prototype.renderStackedBar100=function(a){var d= +a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;q=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.height<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)|| +(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>q&&(h=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,q));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&qq&&(f=q);b.save();r&&this._eventManager.ghostCtx.save(); +b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&& +"number"===typeof t[h].y){q=a.axisX.convertValueToPixel(c);var x;x=0!==a.dataPointYSums[c]?100*(t[h].y/a.dataPointYSums[c]):0;var v=q-a.plotType.plotUnits.length*f/2+a.index*f<<0,y=v+f<<0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=l[c])continue;x=g[c]?g[c]:n;g[c]=s=a.axisY.convertValueToPixel(l[c])}else if(a.axisY.scaleBreaks&&0=t[h].y)w[c]=x+(w[c]?w[c]:0),s=m[c]?m[c]: +n,m[c]=x=a.axisY.convertValueToPixel(w[c]);else if(s=a.axisY.convertValueToPixel(x),0<=t[h].y){var A=g[c]?g[c]:0;x=n+A;s+=A;a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.x2-s)&&(s=e.x2);g[c]=A+(s-x)}else A=m[c]?m[c]:0,x=s-A,s=n-A,a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.x1-x)&&(x=e.x1),m[c]=A+(s-x);c=t[h].color?t[h].color:p._colorSet[h%p._colorSet.length];ea(b,x,v,s,y,c,0,null,u,!1,!1,!1,p.fillOpacity);c=p.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:k, +dataPointIndex:h,x1:x,y1:v,x2:s,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,v,s,y,c,0,null,!1,!1,!1,!1);(t[h].indexLabel||p.indexLabel||t[h].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar100",dataPoint:t[h],dataSeries:p,point:{x:0<=t[h].y?s:x,y:q},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(x,s),y1:v,x2:Math.max(x,s),y2:y},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop", +a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.x2?a.axisY.bounds.x2:n}}};p.prototype.renderArea=function(a){var d,b;function c(){A&&(0=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?y=v:0>a.axisY.viewportMaximum?y=w.y1:0=a.dataSeriesIndexes.length)){var m=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,w=a.axisY.lineCoordinates,h=[],s=this.plotArea,q;g.save();r&&m.save();g.beginPath();g.rect(s.x1,s.y1,s.width,s.height);g.clip();r&&(m.beginPath(),m.rect(s.x1,s.y1,s.width,s.height),m.clip());for(var n=0;na.axisX.dataInfo.viewPortMax&&(!B.connectNullData||!da)))if("number"!==typeof p[k].y)B.connectNullData||(da||d)||c(),da=!0;else{t=a.axisX.convertValueToPixel(x);u=a.axisY.convertValueToPixel(p[k].y);d||da?(!d&&B.connectNullData?(g.setLineDash&&(B.options.nullDataLineDashType||b===B.lineDashType&&B.lineDashType!==B.nullDataLineDashType)&&(d=t,b=u,t=q.x,u=q.y,c(),g.moveTo(q.x,q.y),t=d,u=b,A=q,b=B.nullDataLineDashType,g.setLineDash(Y)),g.lineTo(t,u),r&&m.lineTo(t, +u)):(g.beginPath(),g.moveTo(t,u),r&&(m.beginPath(),m.moveTo(t,u)),A={x:t,y:u}),da=d=!1):(g.lineTo(t,u),r&&m.lineTo(t,u),0==k%250&&c());q={x:t,y:u};kp[k].y===a.axisY.reversed?1:-1,color:z})}c();ia.drawMarkers(h)}}r&&(e.drawImage(this._preRenderCanvas, +0,0,this.width,this.height),g.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&g.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&g.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),g.clearRect(s.x1,s.y1,s.width,s.height),this._eventManager.ghostCtx.restore());g.restore();return{source:e,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear, +animationBase:0}}};p.prototype.renderSplineArea=function(a){function d(){var b=v(x,2);if(0=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum? +t=p:0>a.axisY.viewportMaximum?t=m.y1:0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=a.axisX.lineCoordinates,m=a.axisY.lineCoordinates,l=[],w=this.plotArea;c.save();r&& +e.save();c.beginPath();c.rect(w.x1,w.y1,w.width,w.height);c.clip();r&&(e.beginPath(),e.rect(w.x1,w.y1,w.width,w.height),e.clip());for(var h=0;ha.axisX.dataInfo.viewPortMax&&(!q.connectNullData||!k)))if("number"!==typeof n[f].y)0n[f].y===a.axisY.reversed?1:-1,color:ma});k=!1}d();ia.drawMarkers(l)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(w.x1,w.y1,w.width,w.height), +this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStepArea=function(a){var d,b;function c(){A&&(0=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?y=v:0>a.axisY.viewportMaximum?y=w.y1:0=a.dataSeriesIndexes.length)){var m=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,w=a.axisY.lineCoordinates,h=[],s=this.plotArea,q;g.save();r&&m.save();g.beginPath();g.rect(s.x1,s.y1,s.width,s.height);g.clip();r&&(m.beginPath(),m.rect(s.x1,s.y1,s.width,s.height),m.clip());for(var n=0;na.axisX.dataInfo.viewPortMax&&(!B.connectNullData||!b))){var Z=u;"number"!==typeof k[p].y?(B.connectNullData||(b||d)||c(),b=!0):(t=a.axisX.convertValueToPixel(x),u=a.axisY.convertValueToPixel(k[p].y),d||b?(!d&&B.connectNullData?(g.setLineDash&&(B.options.nullDataLineDashType||Y===B.lineDashType&&B.lineDashType!==B.nullDataLineDashType)&&(d= +t,b=u,t=q.x,u=q.y,c(),g.moveTo(q.x,q.y),t=d,u=b,A=q,Y=B.nullDataLineDashType,g.setLineDash(ca)),g.lineTo(t,Z),g.lineTo(t,u),r&&(m.lineTo(t,Z),m.lineTo(t,u))):(g.beginPath(),g.moveTo(t,u),r&&(m.beginPath(),m.moveTo(t,u)),A={x:t,y:u}),b=d=!1):(g.lineTo(t,Z),r&&m.lineTo(t,Z),g.lineTo(t,u),r&&m.lineTo(t,u),0==p%250&&c()),q={x:t,y:u},pk[p].y===a.axisY.reversed?1:-1,color:z}))}c();ia.drawMarkers(h)}}r&&(e.drawImage(this._preRenderCanvas,0,0,this.width,this.height),g.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&g.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&g.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas, +0,0,this.width,this.height),g.clearRect(s.x1,s.y1,s.width,s.height),this._eventManager.ghostCtx.restore());g.restore();return{source:e,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStackedArea=function(a){function d(){if(!(1>h.length)){for(0=a.dataSeriesIndexes.length)){var e=null,g=null,m=[],l=this.plotArea,w=[],h=[],s=[],q=[],n=0,f,k,p=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),u=this._eventManager.ghostCtx,t,C,x;r&&u.beginPath();c.save();r&&u.save();c.beginPath();c.rect(l.x1,l.y1,l.width,l.height);c.clip();r&&(u.beginPath(),u.rect(l.x1,l.y1,l.width,l.height),u.clip());for(var e=[],v=0;va.axisX.dataInfo.viewPortMax&&(!A.connectNullData||!da)))if("number"!==typeof Z.y)A.connectNullData||(da||C)||d(),da=!0;else{f=a.axisX.convertValueToPixel(g);var oa= +w[g]?w[g]:0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=q[g]&&a.axisY.logarithmic)continue;k=a.axisY.convertValueToPixel(q[g])}else k=a.axisY.convertValueToPixel(Z.y),k-=oa;h.push({x:f,y:p-oa});w[g]=p-k;C||da?(!C&&A.connectNullData?(c.setLineDash&&(A.options.nullDataLineDashType||x===A.lineDashType&&A.lineDashType!==A.nullDataLineDashType)&&(C=h.pop(),x=h[h.length-1],d(),c.moveTo(t.x,t.y),h.push(x),h.push(C),x=A.nullDataLineDashType, +c.setLineDash(Y)),c.lineTo(f,k),r&&u.lineTo(f,k)):(c.beginPath(),c.moveTo(f,k),r&&(u.beginPath(),u.moveTo(f,k))),da=C=!1):(c.lineTo(f,k),r&&u.lineTo(f,k),0==n%250&&(d(),c.moveTo(f,k),r&&u.moveTo(f,k),h.push({x:f,y:p-oa})));t={x:f,y:k};nz[n].y===a.axisY.reversed?1:-1,color:e})}}d();c.moveTo(f,k);r&&u.moveTo(f,k)}delete A.dataPointIndexes}ia.drawMarkers(m);r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&& +c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(l.x1,l.y1,l.width,l.height),u.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStackedArea100=function(a){function d(){for(0=a.dataSeriesIndexes.length)){var e=null,g=null,m=this.plotArea,l=[],w=[],h=[],s=[],q=[],n=0,f,k,p,u,t,C=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),x=this._eventManager.ghostCtx;c.save();r&&x.save();c.beginPath();c.rect(m.x1,m.y1,m.width,m.height);c.clip();r&&(x.beginPath(), +x.rect(m.x1,m.y1,m.width,m.height),x.clip());for(var e=[],v=0;va.axisX.dataInfo.viewPortMax&&(!A.connectNullData|| +!da)))if("number"!==typeof Z.y)A.connectNullData||(da||u)||d(),da=!0;else{var oa;oa=0!==a.dataPointYSums[g]?100*(Z.y/a.dataPointYSums[g]):0;f=a.axisX.convertValueToPixel(g);var la=w[g]?w[g]:0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=q[g]&&a.axisY.logarithmic)continue;k=a.axisY.convertValueToPixel(q[g])}else k=a.axisY.convertValueToPixel(oa),k-=la;h.push({x:f,y:C-la});w[g]=C-k;u||da?(!u&&A.connectNullData?(c.setLineDash&& +(A.options.nullDataLineDashType||t===A.lineDashType&&A.lineDashType!==A.nullDataLineDashType)&&(u=h.pop(),t=h[h.length-1],d(),c.moveTo(p.x,p.y),h.push(t),h.push(u),t=A.nullDataLineDashType,c.setLineDash(Y)),c.lineTo(f,k),r&&x.lineTo(f,k)):(c.beginPath(),c.moveTo(f,k),r&&(x.beginPath(),x.moveTo(f,k))),da=u=!1):(c.lineTo(f,k),r&&x.lineTo(f,k),0==n%250&&(d(),c.moveTo(f,k),r&&x.moveTo(f,k),h.push({x:f,y:C-la})));p={x:f,y:k};nz[n].y===a.axisY.reversed?1:-1,color:e})}}d();c.moveTo(f,k);r&&x.moveTo(f,k)}delete A.dataPointIndexes}ia.drawMarkers(l);r&&(b.drawImage(this._preRenderCanvas,0, +0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(m.x1,m.y1,m.width,m.height),x.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}}; +p.prototype.renderBubble=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this.plotArea,e=0,g,m;b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(c.x1,c.y1,c.width,c.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.clip());for(var l=-Infinity,w=Infinity,h=0;ha.axisX.dataInfo.viewPortMax||"undefined"===typeof n[e].z||(f=n[e].z,f>l&&(l=f),fa.axisX.dataInfo.viewPortMax)&&"number"===typeof n[e].y){g=a.axisX.convertValueToPixel(g);m=a.axisY.convertValueToPixel(n[e].y);var f=n[e].z,u=2*Math.max(Math.sqrt((l===w?p/2:k+(p-k)/(l-w)*(f-w))/Math.PI)<<0,1),f=q.getMarkerProperties(e,b);f.size=u;b.globalAlpha=q.fillOpacity;ia.drawMarker(g,m,b,f.type,f.size,f.color,f.borderColor,f.borderThickness);b.globalAlpha=1;var t=q.dataPointIds[e];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:s, +dataPointIndex:e,x1:g,y1:m,size:u};u=N(t);r&&ia.drawMarker(g,m,this._eventManager.ghostCtx,f.type,f.size,u,u,f.borderThickness);(n[e].indexLabel||q.indexLabel||n[e].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"bubble",dataPoint:n[e],dataSeries:q,point:{x:g,y:m},direction:1,bounds:{x1:g-f.size/2,y1:m-f.size/2,x2:g+f.size/2,y2:m+f.size/2},color:null})}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&& +b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderScatter=function(a){var d= +a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this.plotArea,e=0,g,m;b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(c.x1,c.y1,c.width,c.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.clip());for(var l=0;la.axisX.dataInfo.viewPortMax)&&"number"===typeof s[e].y){g=a.axisX.convertValueToPixel(g);m=a.axisY.convertValueToPixel(s[e].y);var f=h.getMarkerProperties(e,g,m,b);b.globalAlpha=h.fillOpacity;ia.drawMarker(f.x,f.y,f.ctx,f.type,f.size,f.color,f.borderColor,f.borderThickness);b.globalAlpha=1;Math.sqrt((q-g)*(q-g)+(n-m)*(n-m))Math.min(this.plotArea.width,this.plotArea.height)||(q=h.dataPointIds[e],this._eventManager.objectMap[q]={id:q,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:e,x1:g,y1:m},q=N(q),r&&ia.drawMarker(f.x,f.y,this._eventManager.ghostCtx,f.type,f.size,q,q,f.borderThickness),(s[e].indexLabel||h.indexLabel||s[e].indexLabelFormatter||h.indexLabelFormatter)&&this._indexLabels.push({chartType:"scatter",dataPoint:s[e],dataSeries:h,point:{x:g,y:m},direction:1,bounds:{x1:g-f.size/2,y1:m-f.size/ +2,x2:g+f.size/2,y2:m+f.size/2},color:null}),q=g,n=m)}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.restore()); +b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderCandlestick=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=null,m=this.plotArea,l=0,w,h,s,q,n,f,e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,g=this.options.dataPointMaxWidth? +this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.015*this.width,k=a.axisX.dataInfo.minDiff;isFinite(k)||(k=0.3*Math.abs(a.axisX.range));k=this.options.dataPointWidth?this.dataPointWidth:0.7*m.width*(a.axisX.logarithmic?Math.log(k)/Math.log(a.axisX.range):Math.abs(k)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&&e>g&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,g));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&gg&&(k=g);b.save();r&&c.save();b.beginPath();b.rect(m.x1,m.y1,m.width,m.height);b.clip();r&&(c.beginPath(),c.rect(m.x1,m.y1,m.width,m.height),c.clip());for(var p=0;pa.axisX.dataInfo.viewPortMax)&&!u(C[l].y)&&C[l].y.length&& +"number"===typeof C[l].y[0]&&"number"===typeof C[l].y[1]&&"number"===typeof C[l].y[2]&&"number"===typeof C[l].y[3]){w=a.axisX.convertValueToPixel(f);h=a.axisY.convertValueToPixel(C[l].y[0]);s=a.axisY.convertValueToPixel(C[l].y[1]);q=a.axisY.convertValueToPixel(C[l].y[2]);n=a.axisY.convertValueToPixel(C[l].y[3]);var z=w-k/2<<0,y=z+k<<0,g=t.options.fallingColor?t.fallingColor:t._colorSet[0],e=C[l].color?C[l].color:t._colorSet[0],A=Math.round(Math.max(1,0.15*k)),D=0===A%2?0:0.5,aa=t.dataPointIds[l]; +this._eventManager.objectMap[aa]={id:aa,objectType:"dataPoint",dataSeriesIndex:v,dataPointIndex:l,x1:z,y1:h,x2:y,y2:s,x3:w,y3:q,x4:w,y4:n,borderThickness:A,color:e};b.strokeStyle=e;b.beginPath();b.lineWidth=A;c.lineWidth=Math.max(A,4);"candlestick"===t.type?(b.moveTo(w-D,s),b.lineTo(w-D,Math.min(h,n)),b.stroke(),b.moveTo(w-D,Math.max(h,n)),b.lineTo(w-D,q),b.stroke(),ea(b,z,Math.min(h,n),y,Math.max(h,n),C[l].y[0]<=C[l].y[3]?t.risingColor:g,A,e,x,x,!1,!1,t.fillOpacity),r&&(e=N(aa),c.strokeStyle=e,c.moveTo(w- +D,s),c.lineTo(w-D,Math.min(h,n)),c.stroke(),c.moveTo(w-D,Math.max(h,n)),c.lineTo(w-D,q),c.stroke(),ea(c,z,Math.min(h,n),y,Math.max(h,n),e,0,null,!1,!1,!1,!1))):"ohlc"===t.type&&(b.moveTo(w-D,s),b.lineTo(w-D,q),b.stroke(),b.beginPath(),b.moveTo(w,h),b.lineTo(z,h),b.stroke(),b.beginPath(),b.moveTo(w,n),b.lineTo(y,n),b.stroke(),r&&(e=N(aa),c.strokeStyle=e,c.moveTo(w-D,s),c.lineTo(w-D,q),c.stroke(),c.beginPath(),c.moveTo(w,h),c.lineTo(z,h),c.stroke(),c.beginPath(),c.moveTo(w,n),c.lineTo(y,n),c.stroke())); +(C[l].indexLabel||t.indexLabel||C[l].indexLabelFormatter||t.indexLabelFormatter)&&this._indexLabels.push({chartType:t.type,dataPoint:C[l],dataSeries:t,point:{x:z+(y-z)/2,y:a.axisY.reversed?q:s},direction:1,bounds:{x1:z,y1:Math.min(s,q),x2:y,y2:Math.max(s,q)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas, +0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(m.x1,m.y1,m.width,m.height),c.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderBoxAndWhisker=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var e= +null,g=this.plotArea,m=0,l,w,h,s,q,n,f,e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,m=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.015*this.width,k=a.axisX.dataInfo.minDiff;isFinite(k)||(k=0.3*Math.abs(a.axisX.range));k=this.options.dataPointWidth?this.dataPointWidth:0.7*g.width*(a.axisX.logarithmic?Math.log(k)/Math.log(a.axisX.range):Math.abs(k)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&& +e>m&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&mm&&(k=m);b.save();r&&c.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();r&&(c.beginPath(),c.rect(g.x1,g.y1,g.width,g.height),c.clip());for(var p=!1,p=!!a.axisY.reversed,v=0;va.axisX.dataInfo.viewPortMax)&&!u(x[m].y)&&x[m].y.length&&"number"===typeof x[m].y[0]&&"number"===typeof x[m].y[1]&&"number"===typeof x[m].y[2]&&"number"===typeof x[m].y[3]&&"number"===typeof x[m].y[4]&&5===x[m].y.length){l=a.axisX.convertValueToPixel(f);w=a.axisY.convertValueToPixel(x[m].y[0]);h=a.axisY.convertValueToPixel(x[m].y[1]);s=a.axisY.convertValueToPixel(x[m].y[2]); +q=a.axisY.convertValueToPixel(x[m].y[3]);n=a.axisY.convertValueToPixel(x[m].y[4]);var y=l-k/2<<0,A=l+k/2<<0,e=x[m].color?x[m].color:C._colorSet[0],D=Math.round(Math.max(1,0.15*k)),aa=0===D%2?0:0.5,T=x[m].whiskerColor?x[m].whiskerColor:x[m].color?C.whiskerColor?C.whiskerColor:x[m].color:C.whiskerColor?C.whiskerColor:e,Y="number"===typeof x[m].whiskerThickness?x[m].whiskerThickness:"number"===typeof C.options.whiskerThickness?C.whiskerThickness:D,ca=x[m].whiskerDashType?x[m].whiskerDashType:C.whiskerDashType, +da=u(x[m].whiskerLength)?u(C.options.whiskerLength)?k:C.whiskerLength:x[m].whiskerLength,da="number"===typeof da?0>=da?0:da>=k?k:da:"string"===typeof da?parseInt(da)*k/100>k?k:parseInt(da)*k/100:k,Z=1===Math.round(Y)%2?0.5:0,oa=x[m].stemColor?x[m].stemColor:x[m].color?C.stemColor?C.stemColor:x[m].color:C.stemColor?C.stemColor:e,la="number"===typeof x[m].stemThickness?x[m].stemThickness:"number"===typeof C.options.stemThickness?C.stemThickness:D,G=1===Math.round(la)%2?0.5:0,F=x[m].stemDashType?x[m].stemDashType: +C.stemDashType,E=x[m].lineColor?x[m].lineColor:x[m].color?C.lineColor?C.lineColor:x[m].color:C.lineColor?C.lineColor:e,H="number"===typeof x[m].lineThickness?x[m].lineThickness:"number"===typeof C.options.lineThickness?C.lineThickness:D,I=x[m].lineDashType?x[m].lineDashType:C.lineDashType,K=1===Math.round(H)%2?0.5:0,L=C.upperBoxColor,O=C.lowerBoxColor,Q=u(C.options.fillOpacity)?1:C.fillOpacity,P=C.dataPointIds[m];this._eventManager.objectMap[P]={id:P,objectType:"dataPoint",dataSeriesIndex:t,dataPointIndex:m, +x1:y,y1:w,x2:A,y2:h,x3:l,y3:s,x4:l,y4:q,y5:n,borderThickness:D,color:e,stemThickness:la,stemColor:oa,whiskerThickness:Y,whiskerLength:da,whiskerColor:T,lineThickness:H,lineColor:E};b.save();0=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,m,l,w,g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth: +1;m=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.03*this.width;var h=a.axisX.dataInfo.minDiff;isFinite(h)||(h=0.3*Math.abs(a.axisX.range));h=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(h)/Math.log(a.axisX.range):Math.abs(h)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>m&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&& +(this.dataPointMinWidth&&mm&&(h=m);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var s=0;sa.axisX.dataInfo.viewPortMax)&&!u(f[g].y)&&f[g].y.length&&"number"===typeof f[g].y[0]&&"number"===typeof f[g].y[1]){c=a.axisX.convertValueToPixel(w);m=a.axisY.convertValueToPixel(f[g].y[0]);l=a.axisY.convertValueToPixel(f[g].y[1]);var p=a.axisX.reversed?c+a.plotType.totalDataSeries*h/2-(a.previousDataSeriesCount+s)*h<<0:c-a.plotType.totalDataSeries*h/2+(a.previousDataSeriesCount+ +s)*h<<0,v=a.axisX.reversed?p-h<<0:p+h<<0,c=f[g].color?f[g].color:n._colorSet[g%n._colorSet.length];if(m>l){var t=m;m=l;l=t}t=n.dataPointIds[g];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:q,dataPointIndex:g,x1:p,y1:m,x2:v,y2:l};ea(b,p,m,v,l,c,0,c,k,k,!1,!1,n.fillOpacity);c=N(t);r&&ea(this._eventManager.ghostCtx,p,m,v,l,c,0,null,!1,!1,!1,!1);if(f[g].indexLabel||n.indexLabel||f[g].indexLabelFormatter||n.indexLabelFormatter)this._indexLabels.push({chartType:"rangeColumn", +dataPoint:f[g],dataSeries:n,indexKeyword:0,point:{x:p+(v-p)/2,y:f[g].y[1]>=f[g].y[0]?l:m},direction:f[g].y[1]>=f[g].y[0]?-1:1,bounds:{x1:p,y1:Math.min(m,l),x2:v,y2:Math.max(m,l)},color:c}),this._indexLabels.push({chartType:"rangeColumn",dataPoint:f[g],dataSeries:n,indexKeyword:1,point:{x:p+(v-p)/2,y:f[g].y[1]>=f[g].y[0]?m:l},direction:f[g].y[1]>=f[g].y[0]?1:-1,bounds:{x1:p,y1:Math.min(m,l),x2:v,y2:Math.max(m,l)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation= +"source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderError= +function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=a.axisY._position?"left"===a.axisY._position||"right"===a.axisY._position?!1:!0:!1;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=!1,m=this.plotArea,l=0,w,h,s,q,n,f,k,p=a.axisX.dataInfo.minDiff;isFinite(p)||(p=0.3*Math.abs(a.axisX.range));b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(m.x1,m.y1,m.width,m.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(m.x1, +m.y1,m.width,m.height),this._eventManager.ghostCtx.clip());for(var v=0,t=0;tl&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,l));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ll&&(t=l);if(0=T.length?0:T.length>=t?t:T.length:"string"===typeof T.length?parseInt(T.length)*t/100>t?t:parseInt(T.length)*t/100>t:t;T.thickness="number"===typeof T.thickness?0>T.thickness?0:Math.round(T.thickness):2;var Y={color:y[l].stemColor?y[l].stemColor:y[l].color?z.stemColor?z.stemColor:y[l].color:z.stemColor?z.stemColor:e,thickness:y[l].stemThickness?y[l].stemThickness:z.stemThickness,dashType:y[l].stemDashType? +y[l].stemDashType:z.stemDashType};Y.thickness="number"===typeof Y.thickness?0>Y.thickness?0:Math.round(Y.thickness):2;y[l].getTime?k=y[l].x.getTime():k=y[l].x;if(!(ka.axisX.dataInfo.viewPortMax)&&!u(y[l].y)&&y[l].y.length&&"number"===typeof y[l].y[0]&&"number"===typeof y[l].y[1]){var ca=a.axisX.convertValueToPixel(k);c?h=ca:w=ca;ca=a.axisY.convertValueToPixel(y[l].y[0]);c?s=ca:n=ca;ca=a.axisY.convertValueToPixel(y[l].y[1]);c?q=ca:f=ca;c?(n=a.axisX.reversed?h+(A?v: +1)*t/2-(A?D-1:0)*t<<0:h-(A?v:1)*t/2+(A?D-1:0)*t<<0,f=a.axisX.reversed?n-t<<0:n+t<<0):(s=a.axisX.reversed?w+(A?v:1)*t/2-(A?D-1:0)*t<<0:w-(A?v:1)*t/2+(A?D-1:0)*t<<0,q=a.axisX.reversed?s-t<<0:s+t<<0);!c&&n>f&&(ca=n,n=f,f=ca);c&&s>q&&(ca=s,s=q,q=ca);ca=z.dataPointIds[l];this._eventManager.objectMap[ca]={id:ca,objectType:"dataPoint",dataSeriesIndex:x,dataPointIndex:l,x1:Math.min(s,q),y1:Math.min(n,f),x2:Math.max(q,s),y2:Math.max(f,n),isXYSwapped:c,stemProperties:Y,whiskerProperties:T};E(b,Math.min(s,q), +Math.min(n,f),Math.max(q,s),Math.max(f,n),e,T,Y,c);r&&E(this._eventManager.ghostCtx,s,n,q,f,e,T,Y,c);if(y[l].indexLabel||z.indexLabel||y[l].indexLabelFormatter||z.indexLabelFormatter)this._indexLabels.push({chartType:"error",dataPoint:y[l],dataSeries:z,indexKeyword:0,point:{x:c?y[l].y[1]>=y[l].y[0]?s:q:s+(q-s)/2,y:c?n+(f-n)/2:y[l].y[1]>=y[l].y[0]?f:n},direction:y[l].y[1]>=y[l].y[0]?-1:1,bounds:{x1:c?Math.min(s,q):s,y1:c?n:Math.min(n,f),x2:c?Math.max(s,q):q,y2:c?f:Math.max(n,f)},color:e,axisSwapped:c}), +this._indexLabels.push({chartType:"error",dataPoint:y[l],dataSeries:z,indexKeyword:1,point:{x:c?y[l].y[1]>=y[l].y[0]?q:s:s+(q-s)/2,y:c?n+(f-n)/2:y[l].y[1]>=y[l].y[0]?n:f},direction:y[l].y[1]>=y[l].y[0]?1:-1,bounds:{x1:c?Math.min(s,q):s,y1:c?n:Math.min(n,f),x2:c?Math.max(s,q):q,y2:c?f:Math.max(n,f)},color:e,axisSwapped:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height), +a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(m.x1,m.y1,m.width,m.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx: +d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,m,l,w,h,g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;m=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0;var s=a.axisX.dataInfo.minDiff;isFinite(s)||(s=0.3*Math.abs(a.axisX.range));s=this.options.dataPointWidth?this.dataPointWidth:0.9* +(e.height*(a.axisX.logarithmic?Math.log(s)/Math.log(a.axisX.range):Math.abs(s)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>m&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&mm&&(s=m);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(), +this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var q=0;qa.axisX.dataInfo.viewPortMax)&&!u(k[g].y)&&k[g].y.length&&"number"===typeof k[g].y[0]&&"number"===typeof k[g].y[1]){m=a.axisY.convertValueToPixel(k[g].y[0]); +l=a.axisY.convertValueToPixel(k[g].y[1]);w=a.axisX.convertValueToPixel(h);w=a.axisX.reversed?w+a.plotType.totalDataSeries*s/2-(a.previousDataSeriesCount+q)*s<<0:w-a.plotType.totalDataSeries*s/2+(a.previousDataSeriesCount+q)*s<<0;var v=a.axisX.reversed?w-s<<0:w+s<<0;m>l&&(c=m,m=l,l=c);c=k[g].color?k[g].color:f._colorSet[g%f._colorSet.length];ea(b,m,w,l,v,c,0,null,p,!1,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g, +x1:m,y1:w,x2:l,y2:v};c=N(c);r&&ea(this._eventManager.ghostCtx,m,w,l,v,c,0,null,!1,!1,!1,!1);if(k[g].indexLabel||f.indexLabel||k[g].indexLabelFormatter||f.indexLabelFormatter)this._indexLabels.push({chartType:"rangeBar",dataPoint:k[g],dataSeries:f,indexKeyword:0,point:{x:k[g].y[1]>=k[g].y[0]?m:l,y:w+(v-w)/2},direction:k[g].y[1]>=k[g].y[0]?-1:1,bounds:{x1:Math.min(m,l),y1:w,x2:Math.max(m,l),y2:v},color:c}),this._indexLabels.push({chartType:"rangeBar",dataPoint:k[g],dataSeries:f,indexKeyword:1,point:{x:k[g].y[1]>= +k[g].y[0]?l:m,y:w+(v-w)/2},direction:k[g].y[1]>=k[g].y[0]?1:-1,bounds:{x1:Math.min(m,l),y1:w,x2:Math.max(m,l),y2:v},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1, +e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeArea=function(a){function d(){if(C){var a=null;0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=[],m=this.plotArea;c.save();r&&e.save();c.beginPath();c.rect(m.x1,m.y1,m.width,m.height);c.clip();r&&(e.beginPath(),e.rect(m.x1,m.y1,m.width,m.height),e.clip());for(var l=0;la.axisX.dataInfo.viewPortMax&&(!s.connectNullData||!T)))if(null!==q[f].y&&q[f].y.length&&"number"===typeof q[f].y[0]&&"number"===typeof q[f].y[1]){k=a.axisX.convertValueToPixel(t);p=a.axisY.convertValueToPixel(q[f].y[0]);u=a.axisY.convertValueToPixel(q[f].y[1]);n||T?(s.connectNullData&&!n?(c.setLineDash&&(s.options.nullDataLineDashType||A===s.lineDashType&&s.lineDashType!==s.nullDataLineDashType)&&(w[w.length- +1].newLineDashArray=D,A=s.nullDataLineDashType,c.setLineDash(z)),c.lineTo(k,p),r&&e.lineTo(k,p),w.push({x:k,y:u})):(c.beginPath(),c.moveTo(k,p),C={x:k,y:p},w=[],w.push({x:k,y:u}),r&&(e.beginPath(),e.moveTo(k,p))),T=n=!1):(c.lineTo(k,p),w.push({x:k,y:u}),r&&e.lineTo(k,p),0==f%250&&d());t=s.dataPointIds[f];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:f,x1:k,y1:p,y2:u};fq[f].y[1]===a.axisY.reversed?-1:1,color:x}),this._indexLabels.push({chartType:"rangeArea",dataPoint:q[f],dataSeries:s,indexKeyword:1,point:{x:k, +y:u},direction:q[f].y[0]>q[f].y[1]===a.axisY.reversed?1:-1,color:x})}else T||n||d(),T=!0;d();ia.drawMarkers(g)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(m.x1,m.y1, +m.width,m.height),this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderRangeSplineArea=function(a){function d(a,b){var d=v(u,2);if(0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=[],m=this.plotArea;c.save();r&&e.save();c.beginPath();c.rect(m.x1,m.y1,m.width,m.height);c.clip();r&&(e.beginPath(),e.rect(m.x1,m.y1,m.width, +m.height),e.clip());for(var l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!f)))if(null!==k[q].y&&k[q].y.length&&"number"===typeof k[q].y[0]&&"number"===typeof k[q].y[1]){n=a.axisX.convertValueToPixel(n);f=a.axisY.convertValueToPixel(k[q].y[0]);p=a.axisY.convertValueToPixel(k[q].y[1]);var E=h.dataPointIds[q];this._eventManager.objectMap[E]={id:E,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:q, +x1:n,y1:f,y2:p};u[u.length]={x:n,y:f};z[z.length]={x:n,y:p};q=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx,e=null,g=this.plotArea,m=0,l,k,h,s,q=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),m=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;k=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width,0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0;var n= +a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.options.dataPointWidth?this.dataPointWidth:0.6*(g.width*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&m>k&&(m=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,k));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&kk&&(n=k);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(g.x1,g.y1,g.width,g.height),this._eventManager.ghostCtx.clip());for(var f=0;fh&&(e=k,k=h,h=e);a.axisY.reversed&&(e=k,k=h,h=e);e=u.dataPointIds[m];this._eventManager.objectMap[e]={id:e,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:m,x1:l,y1:k,x2:F,y2:h}; +var T=v[m].color?v[m].color:0v[m].y===a.axisY.reversed?1:-1,bounds:{x1:l,y1:Math.min(k,h),x2:F,y2:Math.max(k,h)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height), +b.clearRect(g.x1,g.y1,g.width,g.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};var ja=function(a,d,b,c,e,g,m,l,k){if(!(0>b)){"undefined"===typeof l&&(l=1);if(!r){var h=Number((m%(2*Math.PI)).toFixed(8));Number((g%(2*Math.PI)).toFixed(8))===h&&(m-=1E-4)}a.save();a.globalAlpha=l;"pie"===e?(a.beginPath(),a.moveTo(d.x,d.y),a.arc(d.x,d.y,b,g,m,!1),a.fillStyle=c,a.strokeStyle= +"white",a.lineWidth=2,a.closePath(),a.fill()):"doughnut"===e&&(a.beginPath(),a.arc(d.x,d.y,b,g,m,!1),0<=k&&a.arc(d.x,d.y,k*b,m,g,!0),a.closePath(),a.fillStyle=c,a.strokeStyle="white",a.lineWidth=2,a.fill());a.globalAlpha=1;a.restore()}};p.prototype.renderPie=function(a){function d(){if(h&&s){for(var a=0,b=0,c=0,e=0,d=0;dMath.PI/2-t&&m.midAngle +m.midAngle)c=d;a++}else if(m.midAngle>3*Math.PI/2-t&&m.midAngle<3*Math.PI/2+t){if(0===b||f[e].midAngle>m.midAngle)e=d;b++}m.hemisphere=g>Math.PI/2&&g<=3*Math.PI/2?"left":"right";m.indexLabelTextBlock=new ka(k.plotArea.ctx,{fontSize:m.indexLabelFontSize,fontFamily:m.indexLabelFontFamily,fontColor:m.indexLabelFontColor,fontStyle:m.indexLabelFontStyle,fontWeight:m.indexLabelFontWeight,horizontalAlign:"left",backgroundColor:m.indexLabelBackgroundColor,maxWidth:m.indexLabelMaxWidth,maxHeight:m.indexLabelWrap? +5*m.indexLabelFontSize:1.5*m.indexLabelFontSize,text:m.indexLabelText,padding:0,textBaseline:"top"});m.indexLabelTextBlock.measureText()}l=g=0;q=!1;for(d=0;dMath.PI/2-t&&m.midAngle3*Math.PI/2-t&&m.midAngle<3*Math.PI/2+t)&&(l<=b/2&&!q?(m.hemisphere="left",l++):(m.hemisphere="right",q=!0))}}function b(a){var b= +k.plotArea.ctx;b.clearRect(n.x1,n.y1,n.width,n.height);b.fillStyle=k.backgroundColor;b.fillRect(n.x1,n.y1,n.width,n.height);for(b=0;bc){var d=0.07*A*Math.cos(f[b].midAngle),g=0.07*A*Math.sin(f[b].midAngle),m=!1;if(s[b].exploded){if(1E-9a.indexLabelTextBlock.y?d-e:c-f}function e(a){for(var b=null,e=1;ec(f[b],f[a])||("right"===f[a].hemisphere?f[b].indexLabelTextBlock.y>=f[a].indexLabelTextBlock.y:f[b].indexLabelTextBlock.y<=f[a].indexLabelTextBlock.y)))break;else b=null;return b}function g(a,b,d){d=(d||0)+1;if(1E3< +d)return 0;b=b||0;var m=0,h=x.y-1*r,l=x.y+1*r;if(0<=a&&ab&&n.indexLabelTextBlock.yl)return 0;var k=0,q=0,q=k=k=0;0>b?n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2>h&&n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2+bl&&(b=n.indexLabelTextBlock.y+ +n.indexLabelTextBlock.height/2+b-l);b=n.indexLabelTextBlock.y+b;h=0;h="right"===n.hemisphere?x.x+Math.sqrt(Math.pow(r,2)-Math.pow(b-x.y,2)):x.x-Math.sqrt(Math.pow(r,2)-Math.pow(b-x.y,2));q=x.x+A*Math.cos(n.midAngle);k=x.y+A*Math.sin(n.midAngle);k=Math.sqrt(Math.pow(h-q,2)+Math.pow(b-k,2));q=Math.acos(A/r);k=Math.acos((r*r+A*A-k*k)/(2*A*r));b=kc(f[h],f[a])||("right"===f[a].hemisphere?f[h].indexLabelTextBlock.y<=f[a].indexLabelTextBlock.y:f[h].indexLabelTextBlock.y>=f[a].indexLabelTextBlock.y)))break;else h=null;q=h;k=e(a);l=h=0;0>b?(l="right"===n.hemisphere?q:k,m=b,null!==l&&(q=-b,b=n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2-(f[l].indexLabelTextBlock.y+f[l].indexLabelTextBlock.height/2),b-q+h.toFixed(C)&&(m=b>p?-(b-p):-(q-(l-h)))))):0p?b-p:q-(h-l)))));m&&(d=n.indexLabelTextBlock.y+m,b=0,b="right"===n.hemisphere?x.x+Math.sqrt(Math.pow(r,2)-Math.pow(d-x.y,2)):x.x-Math.sqrt(Math.pow(r,2)-Math.pow(d-x.y,2)),n.midAngle>Math.PI/2-t&&n.midAngleh.indexLabelTextBlock.x?b=h.indexLabelTextBlock.x-15:"right"===n.hemisphere&&("left"===a.hemisphere&&b3*Math.PI/2-t&&n.midAngle<3*Math.PI/2+t&&(h=(a-1+f.length)%f.length,h=f[h],a=f[(a+1+f.length)%f.length],"right"===n.hemisphere&&"left"===h.hemisphere&&ba.indexLabelTextBlock.x)&&(b=a.indexLabelTextBlock.x- +15)),n.indexLabelTextBlock.y=d,n.indexLabelTextBlock.x=b,n.indexLabelAngle=Math.atan2(n.indexLabelTextBlock.y-x.y,n.indexLabelTextBlock.x-x.x))}return m}function m(){var a=k.plotArea.ctx;a.fillStyle="grey";a.strokeStyle="grey";a.font="16px Arial";a.textBaseline="middle";for(var b=a=0,d=0,m=!0,b=0;10>b&&(1>b||0z){for(var E=u=0,H=0;Hu?l.indexLabelText="":l.indexLabelTextBlock.maxWidth=0.85*u,0.3*l.indexLabelTextBlock.maxWidthd&&(d=y)),y=y=0,0d&&(d=y)));var K=function(a, +b,c){for(var e=[],d=0;e.push(f[b]),b!==c;b=(b+1+s.length)%s.length);e.sort(function(a,b){return a.y-b.y});for(b=0;bz){n=t.indexLabelTextBlock.x;var k=t.indexLabelTextBlock.y-t.indexLabelTextBlock.height/ +2,w=t.indexLabelTextBlock.y+t.indexLabelTextBlock.height/2,p=h.indexLabelTextBlock.y-h.indexLabelTextBlock.height/2,u=h.indexLabelTextBlock.x+h.indexLabelTextBlock.width,r=h.indexLabelTextBlock.y+h.indexLabelTextBlock.height/2;n=t.indexLabelTextBlock.x+t.indexLabelTextBlock.widthu+q||k>r+q||wa&&(a=l),m!==a&&(b=m,d+=-z),0===l%Math.max(s.length/10,3)&&(g=!0)):g=!0;g&&(0=a.dataSeriesIndexes.length)){var h= +this.data[a.dataSeriesIndexes[0]],s=h.dataPoints,q=10,n=this.plotArea,f=h.dataPointEOs,p=2,r,v=1.3,t=20/180*Math.PI,C=6,x={x:(n.x2+n.x1)/2,y:(n.y2+n.y1)/2},z=0;a=!1;for(var y=0;ya&&(e=a,d=!0);var g=s[b].color?s[b].color:h._colorSet[b%h._colorSet.length];e>c&&ja(k.plotArea.ctx, +f[b].center,f[b].radius,g,h.type,c,e,h.fillOpacity,f[b].percentInnerRadius);if(d)break}l()},function(){k.disableToolTip=!1;k._animator.animate(0,k.animatedRender?500:0,function(a){b(a);l()})})}}};var ra=function(a,d,b,c){"undefined"===typeof b&&(b=1);0>=Math.round(d.y4-d.y1)||(a.save(),a.globalAlpha=b,a.beginPath(),a.moveTo(Math.round(d.x1),Math.round(d.y1)),a.lineTo(Math.round(d.x2),Math.round(d.y2)),a.lineTo(Math.round(d.x3),Math.round(d.y3)),a.lineTo(Math.round(d.x4),Math.round(d.y4)),"undefined"!== +d.x5&&(a.lineTo(Math.round(d.x5),Math.round(d.y5)),a.lineTo(Math.round(d.x6),Math.round(d.y6))),a.closePath(),a.fillStyle=c?c:d.color,a.fill(),a.globalAplha=1,a.restore())};p.prototype.renderFunnel=function(a){function d(){for(var a=0,b=[],c=0;ck?(k=c,m=(b+k)*(d-h)/2,a-=m,n=d-h,h+=d-h,n+=0==k?0:a/k,h+=a/k,m=!0):(n=(Math.abs(ba)*b-Math.sqrt(k))/2,k=b-2*n/Math.abs(ba),h+=n,h>d&&(h-=n, +k=c,m=(b+k)*(d-h)/2,a-=m,n=d-h,h+=d-h,n+=a/k,h+=a/k,m=!0),b=k)),e.push(n);return e}function c(){if(t&&C){for(var a,b,c,e,d,g,l,h,m,n,k,q,s,w,p=[],B=[],x={percent:null,total:null},v=null,y=0;yp[y]&&(p[y]=y!==fa?t.reversed?P[y].x3-P[y].x4:P[y].x2-P[y].x1:P[y].x2-P[y].x1,p[y]/=2));s=b.indexLabelMaxWidth?b.indexLabelMaxWidth:t.options.indexLabelMaxWidth?t.indexLabelMaxWidth:p[y];if(s>p[y]||0>s)s=p[y];B[y]="inside"===t.indexLabelPlacement?P[y].height:!1;x=z.getPercentAndTotal(t,b);if(t.indexLabelFormatter||b.indexLabelFormatter)v={chart:z.options,dataSeries:t,dataPoint:b,total:x.total,percent:x.percent};b=b.indexLabelFormatter?b.indexLabelFormatter(v):b.indexLabel? +z.replaceKeywordsWithValue(b.indexLabel,b,t,y):t.indexLabelFormatter?t.indexLabelFormatter(v):t.indexLabel?z.replaceKeywordsWithValue(t.indexLabel,b,t,y):b.label?b.label:"";0>=n&&(n=0);1E3>s&&1E3-sl?l:t.indexLabelMaxWidth:l,h=J.length-1;0<=h;h--){g=C[J[h].id];c=J[h];e=c.textBlock;b=(a=n(h)b.y&&(d=!0);c=g.indexLabelMaxWidth||l;if(c>l||0>c)c=l;f.push(c)}if(d)for(h=J.length-1;0<=h;h--)a=P[h],J[h].textBlock.maxWidth= +f[f.length-(h+1)],J[h].textBlock.measureText(),J[h].textBlock.x=L-l,c=J[h].textBlock.heightpa+D&&(J[h].textBlock.y=pa+D-J[h].height),J[h].textBlock.ywa+D&&(J[h].textBlock.y=wa+D-J[h].height))}function g(){var a,b,c,e;if("inside"!==t.indexLabelPlacement)for(var d=0;dDa?f(c).x2+1:(a.x2+a.x3)/2+1:(a.x2+a.x3)/2+1:"undefined"!==typeof a.x5?cpa+D&&(J[d].textBlock.y=pa+D-J[d].height),J[d].textBlock.ywa+D&&(J[d].textBlock.y=wa+D-J[d].height)));else for(d=0;d=c?(b=d!=fa?(a.x4+a.x3)/2-e/2:(a.x5+a.x4)/2-e/2,c=d!=fa?(a.y1+a.y3)/2-c/2:(a.y1+a.y4)/2-c/2,J[d].textBlock.x=b, +J[d].textBlock.y=c):J[d].isDirty=!0)}function m(){function a(b,c){var d;if(0>b||b>=J.length)return 0;var e,f=J[b].textBlock;if(0>c){c*=-1;e=q(b);d=l(e,b);if(d>=c)return f.y-=c,c;if(0==b)return 0=c)return f.y+=c,c;if(b==P.length-1)return 0e)&&(l=n(s),!(l>=J.length-1)&&J[s].textBlock.y+J[s].height+ga>J[l].textBlock.y&&(J[s].textBlock.y=J[s].textBlock.y+J[s].height-e>e-J[s].textBlock.y?e+1:e-J[s].height-1))}for(l=P.length-1;0e&&(e=0,J[e].isDirty))break;if(J[l].textBlock.y=f){f=0;h+=J[f].height;break}e=q(f); +if(0>e){f=0;h+=J[f].height;break}}if(f!=l){g=J[f].textBlock.y;a-=g;a=h-a;g=c(a,d,f);break}}}return g}function c(a,b,d){var e=[],f=0,g=0;for(a=Math.abs(a);d<=b;d++)e.push(P[d]);e.sort(function(a,b){return a.height-b.height});for(d=0;d+m.y.toFixed(6))&&(d=g.y+d+ga-m.y,e=a(w,-d),ea?t.reversed?wa-D:pa-D:J[a].textBlock.y+J[a].height+ga)}function k(a,b,c){var d,e,f,l=[],m=D,n=[];-1!==b&&(0<=W.indexOf(b)?(e=W.indexOf(b),W.splice(e,1)):(W.push(b),W=W.sort(function(a,b){return a-b})));if(0===W.length)l= +ia;else{e=D*(1!=W.length||0!=W[0]&&W[0]!=P.length-1?2:1)/h();for(var q=0;qn&&(n*=-1),c.y1+=b-n[d],c.y2+=b-n[d],c.y3+=b-n[d],c.y4+=b-n[d],c.y5&&(c.y5+=b-n[d],c.y6+=b-n[d]),n[d]=b}};a._animator.animate(0,c,function(c){var d=a.plotArea.ctx||a.ctx;ja=!0;d.clearRect(x.x1,x.y1,x.x2-x.x1,x.y2-x.y1);d.fillStyle=a.backgroundColor;d.fillRect(x.x1,x.y1,x.width,x.height);w.changeSection(c,b);var e={};e.dataSeries=t;e.dataPoint=t.reversed?t.dataPoints[C.length-1-b]:t.dataPoints[b];e.index=t.reversed?C.length-1-b:b;a.toolTip.highlightObjects([e]); +for(e=0;ea){b=P[c];break}return b?(a=b.y6?a>b.y6?b.x3+(b.x4-b.x3)/(b.y4-b.y3)*(a-b.y3):b.x2+(b.x3-b.x2)/(b.y3-b.y2)*(a-b.y2):b.x2+(b.x3-b.x2)/(b.y3-b.y2)*(a-b.y2), +{x1:a,x2:a}):-1}function p(a){for(var b=0;b=a.dataSeriesIndexes.length)){for(var t=this.data[a.dataSeriesIndexes[0]],C=t.dataPoints,x=this.plotArea,D=0.025*x.width,y=0.01*x.width,A=0,F=x.height-2*D,E=Math.min(x.width-2*y,2.8*x.height),H=!1,I=0;IF?N=F:0>=N&&(N=0),G>a?G=a-0.5:0>=G&&(G=0)):"pyramid"===t.type&&(G=N=0,t.reversed=t.reversed?!1:!0);var y=I+a/2,$=I,V=I+a,pa=t.reversed?Z:O,K=y-G/2,ea=y+G/2,Da=t.reversed?O+N:Z- +N,wa=t.reversed?O:Z;a=[];var y=[],P=[],E=[],X=O,fa,ba=(Da-pa)/(K-$),ha=-ba,I="area"===(t.valueRepresents?t.valueRepresents:"height")?b():d();if(-1!==I){if(t.reversed)for(E.push(X),G=I.length-1;0a&&(A=a));for(G=0;G\n');c.document.close();setTimeout(function(){c.focus();c.print();setTimeout(function(){b._canvasJSContainer.removeChild(d)},1E3)},500)};p.prototype.getPercentAndTotal=function(a,d){var b=null,c=null, +e=null;if(0<=a.type.indexOf("stacked"))c=0,b=d.x.getTime?d.x.getTime():d.x,b in a.plotUnit.yTotals&&(c=a.plotUnit.yTotals[b],e=isNaN(d.y)?0:100*(d.y/c));else if("pie"===a.type||"doughnut"===a.type||"funnel"===a.type||"pyramid"===a.type){for(b=c=0;b=m||"undefined"=== +typeof m||0>=v||"undefined"===typeof v)){if("horizontal"===this.orientation){n.textBlock=new ka(this.ctx,{x:0,y:0,maxWidth:v,maxHeight:this.itemWrap?m:this.lineHeight,angle:0,text:n.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"});n.textBlock.measureText();null!==this.itemWidth&&(n.textBlock.width=this.itemWidth-(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"=== +n.chartType?2*0.1*this.lineHeight:0)));if(!q||q.width+Math.round(n.textBlock.width+r+l+(0===q.width?0:this.horizontalSpacing)+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))>g)q={items:[],width:0},h.push(q),this.height+=f,f=0;f=Math.max(f,n.textBlock.height)}else n.textBlock=new ka(this.ctx,{x:0,y:0,maxWidth:x,maxHeight:!0===this.itemWrap?m:1.5*this.fontSize,angle:0,text:n.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily, +fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"}),n.textBlock.measureText(),null!==this.itemWidth&&(n.textBlock.width=this.itemWidth-(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))),this.height>0,0),this.dataPoints.length):0):(s=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,s=0>0,0),this.dataPoints.length):0));for(;;){g=0a?c.x/a:a/c.x: +Math.abs(c.x-a);qs-e&&s+e>=this.dataPoints.length)break;-1===m?(e++,m=1):m=-1}return d||b.dataPoint.x!==a?d&&null!==b.dataPoint?b:null:b};F.prototype.getDataPointAtXY=function(a,d,b){if(!this.dataPoints||0===this.dataPoints.length||athis.chart.plotArea.x2||dthis.chart.plotArea.y2)return null;b=b||!1;var c=[],e=0,g=0,m=1,l=!1,k=Infinity, +h=0,s=0,q=0;if("none"!==this.chart.plotInfo.axisPlacement)if(q=(this.chart.axisX[0]?this.chart.axisX[0]:this.chart.axisX2[0]).getXValueAt({x:a,y:d}),this.axisX.logarithmic)var n=Math.log(this.dataPoints[this.dataPoints.length-1].x/this.dataPoints[0].x),q=1>0,0),this.dataPoints.length):0;else n=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,q=0> +0,0),this.dataPoints.length):0;for(;;){g=0=n.x1&&(a<=n.x2&&d>=n.y1&&d<=n.y2)&&(c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1- +a),Math.abs(n.x2-a),Math.abs(n.y1-d),Math.abs(n.y2-d))}),l=!0);break;case "line":case "stepLine":case "spline":case "area":case "stepArea":case "stackedArea":case "stackedArea100":case "splineArea":case "scatter":var u=na("markerSize",f,this)||4,r=b?20:u,p=Math.sqrt(Math.pow(n.x1-a,2)+Math.pow(n.y1-d,2));p<=r&&c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:p});n=Math.abs(n.x1-a);n<=k?k=n:0r&&(p=Math.atan2(d-u.y,a-u.x),0>p&&(p+=2*Math.PI),p=Number(((180*(p/Math.PI)%360+360)%360).toFixed(12)),u=Number(((180*(n.startAngle/Math.PI)%360+360)%360).toFixed(12)),r=Number(((180*(n.endAngle/Math.PI)%360+360)%360).toFixed(12)),0===r&&1=r&&0!==f.y&&(r+=360,pu&&pp.y1&&dp.y6?(g=p.x6+(p.x5-p.x6)/(p.y5-p.y6)*(d-p.y6),p=p.x3+(p.x4-p.x3)/(p.y4-p.y3)*(d-p.y3)):(g=p.x1+(p.x6-p.x1)/(p.y6-p.y1)*(d-p.y1),p=p.x2+(p.x3-p.x2)/(p.y3-p.y2)*(d-p.y2)):(g=p.x1+(p.x4-p.x1)/(p.y4-p.y1)*(d-p.y1),p=p.x2+(p.x3-p.x2)/(p.y3-p.y2)*(d-p.y2)),a>g&&a=n.x1-n.borderThickness/2&&a<=n.x2+n.borderThickness/2&&d>=n.y4-n.borderThickness/2&&d<=n.y1+n.borderThickness/ +2||Math.abs(n.x2-a+n.x1-a)=n.y1&&d<=n.y4)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0;break;case "candlestick":if(a>=n.x1-n.borderThickness/2&&a<=n.x2+n.borderThickness/2&&d>=n.y2-n.borderThickness/2&&d<=n.y3+n.borderThickness/2||Math.abs(n.x2-a+n.x1-a)=n.y1&&d<=n.y4)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a), +Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0;break;case "ohlc":if(Math.abs(n.x2-a+n.x1-a)=n.y2&&d<=n.y3||a>=n.x1&&a<=(n.x2+n.x1)/2&&d>=n.y1-n.borderThickness/2&&d<=n.y1+n.borderThickness/2||a>=(n.x1+n.x2)/2&&a<=n.x2&&d>=n.y4-n.borderThickness/2&&d<=n.y4+n.borderThickness/2)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0}if(l||1E3q-e&&q+e>= +this.dataPoints.length)break;-1===m?(e++,m=1):m=-1}a=null;for(d=0;dq[f].endValue;f++);a=f=q[f].startValue&&b<=q[f].endValue;p=b;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:p,label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:ba(p,this.valueFormatString,this.chart._cultureInfo),a=new ka(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:m,angle:this.labelAngle,text:this.prefix+a+this.suffix,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius, +horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle",borderThickness:0}),this._labels.push({position:p,textBlock:a,effectiveHeight:null}))}f=n;for(b=this.intervalStartPosition;b<=e;b=parseFloat(1E-12>this.interval?this.logarithmic&&this.equidistantInterval?b*Math.pow(this.logarithmBase,this.interval):b+this.interval:(this.logarithmic&&this.equidistantInterval? +b*Math.pow(this.logarithmBase,this.interval):b+this.interval).toFixed(12))){for(;fq[f].endValue;f++);a=f=q[f].startValue&&b<=q[f].endValue;p=b;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:p,label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:ba(p,this.valueFormatString,this.chart._cultureInfo),a=new ka(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:m,angle:this.labelAngle,text:this.prefix+a+this.suffix, +horizontalAlign:"left",backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"}),this._labels.push({position:p,textBlock:a,effectiveHeight:null}))}}else for(this.intervalStartPosition=this.getLabelStartPoint(new Date(this.viewportMinimum), +this.intervalType,this.interval),e=Ya(new Date(this.viewportMaximum),this.interval,this.intervalType),f=n,b=this.intervalStartPosition;bq[f].endValue;f++);p=a;a=f=q[f].startValue&&a<=q[f].endValue;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:new Date(p),label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:Ca(p,this.valueFormatString,this.chart._cultureInfo), +a=new ka(this.ctx,{x:0,y:0,maxWidth:g,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,maxHeight:m,angle:this.labelAngle,text:this.prefix+a+this.suffix,horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"}),this._labels.push({position:p,textBlock:a,effectiveHeight:null, +breaksLabelType:void 0}))}if("bottom"===this._position||"top"===this._position)l=this.logarithmic&&!this.equidistantInterval&&2<=this._labels.length?this.lineCoordinates.width*Math.log(Math.min(this._labels[this._labels.length-1].position/this._labels[this._labels.length-2].position,this._labels[1].position/this._labels[0].position))/Math.log(this.range):this.lineCoordinates.width/(this.logarithmic&&this.equidistantInterval?Math.log(this.range)/Math.log(this.logarithmBase):Math.abs(this.range))*S[this.intervalType+ +"Duration"]*this.interval,g="undefined"===typeof this.options.labelMaxWidth?0.5*this.chart.width>>0:this.options.labelMaxWidth,this.chart.panEnabled||(m="undefined"===typeof this.options.labelWrap||this.labelWrap?0.8*this.chart.height>>0:1.5*this.labelFontSize);else if("left"===this._position||"right"===this._position)l=this.logarithmic&&!this.equidistantInterval&&2<=this._labels.length?this.lineCoordinates.height*Math.log(Math.min(this._labels[this._labels.length-1].position/this._labels[this._labels.length- +2].position,this._labels[1].position/this._labels[0].position))/Math.log(this.range):this.lineCoordinates.height/(this.logarithmic&&this.equidistantInterval?Math.log(this.range)/Math.log(this.logarithmBase):Math.abs(this.range))*S[this.intervalType+"Duration"]*this.interval,this.chart.panEnabled||(g="undefined"===typeof this.options.labelMaxWidth?0.3*this.chart.width>>0:this.options.labelMaxWidth),m="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize; +for(c=0;cthis.labelAngle?this.labelAngle-=180:270<=this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360)),"bottom"===this._position||"top"===this._position)if(g=0.9*l>>0,n=0,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize= +this.labelFontSize;this.sessionVariables.labelMaxWidth=g;this.sessionVariables.labelMaxHeight=m;this.sessionVariables.labelAngle=this.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;bn&&(v=b,n=p.width)}b=0;for(b=this.intervalStartPosition< +this.viewportMinimum?1:0;b>0>2*g&&(this.sessionVariables.labelAngle=-25)):(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle):u(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelMaxWidth= +g,B.width+d.width>>0>2*g&&(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p)):(this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelWrap=this.labelWrap);else{if(u(this.options.labelWrap))if(!u(this.options.labelMaxWidth))this.options.labelMaxWidth>0,f=this.labelFontSize,nq&&(q=c-2*g,c>=2*g&&c<2.2*g?(this.sessionVariables.labelMaxWidth=g,u(this.options.labelFontSize)&&12=2.2*g&&c<2.8*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelFontSize=f):c>=2.8*g&&c<3.2*g?(this.sessionVariables.labelMaxWidth=Math.max(g,n),this.sessionVariables.labelWrap=!0,u(this.options.labelFontSize)&&12=3.2*g&&c<3.6*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelFontSize=this.labelFontSize):c>3.6*g&&c<5*g?(u(this.options.labelFontSize)&&125*g&&(this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelFontSize=f,this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelAngle=this.labelAngle));else if(v===b&&(0===v&&n+this._labels[v+1].textBlock.measureText().width-2*g>q||v===this._labels.length-1&&n+this._labels[v-1].textBlock.measureText().width-2*g>q||0q&&n+this._labels[v-1].textBlock.measureText().width- +2*g>q))q=0===v?n+this._labels[v+1].textBlock.measureText().width-2*g:n+this._labels[v-1].textBlock.measureText().width-2*g,this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p;else if(0===q)for(this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,c=0;c>0>2*g&&(this.sessionVariables.labelAngle=-25))}else(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxHeight=0===this.labelAngle?m:Math.min((c-g*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/ +Math.sin(Math.PI/180*Math.abs(this.labelAngle)),c),p=0!=this.labelAngle?(h-(k+a.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/Math.sin(Math.PI/180*Math.abs(this.labelAngle)):g,this.sessionVariables.labelMaxHeight=m=this.labelWrap?(h-p*Math.sin(Math.PI/180*Math.abs(this.labelAngle)))/Math.cos(Math.PI/180*Math.abs(this.labelAngle)):1.5*this.labelFontSize,u(this.options.labelWrap))?u(this.options.labelWrap)&&(this.labelWrap&&!u(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap= +this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p,this.sessionVariables.labelMaxHeight=m):(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelMaxHeight=c<0.9*l?0.9*l:c,this.sessionVariables.labelWrap=this.labelWrap)):(this.options.labelWrap?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p): +(u(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p,this.sessionVariables.labelWrap=this.labelWrap),this.sessionVariables.labelMaxHeight=m)}for(c=0;c>0:this.options.labelMaxWidth,m="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize=this.labelFontSize;this.sessionVariables.labelMaxWidth= +g;this.sessionVariables.labelMaxHeight=m;this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;b>0,l-2*m>n&&(n=l-2*m,l>=2*m&&l<2.4*m?(u(this.options.labelFontSize)&&12=2.4*m&&l<2.8*m?(this.sessionVariables.labelMaxHeight=c,this.sessionVariables.labelFontSize=this.labelFontSize,this.sessionVariables.labelWrap=!0):l>=2.8*m&&l<3.2*m?(this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelWrap=!0,u(this.options.labelFontSize)&&12< +this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle):l>=3.2*m&&l<3.6*m?(this.sessionVariables.labelMaxHeight=c,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelFontSize=this.labelFontSize):l>3.6*m&&l<10*m?(u(this.options.labelFontSize)&& +1210*m&&l<50*m&&(u(this.options.labelFontSize)&&12this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=c):(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:g,this.sessionVariables.labelMaxHeight=0===this.labelAngle?m:c,u(this.options.labelMaxWidth)&& +(this.sessionVariables.labelAngle=this.labelAngle))):this.options.labelWrap?(this.sessionVariables.labelMaxHeight=0===this.labelAngle?m:c,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=g):(this.sessionVariables.labelMaxHeight=m,u(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=this.labelWrap)}for(c=0;c>0:1.5*this.labelFontSize;if("left"===this._position||"right"===this._position)z=u(g.options.labelWrap)?this.sessionVariables.labelMaxHeight:g.labelWrap?0.8*this.chart.width>>0:1.5*this.labelFontSize;u(g.labelBackgroundColor)&&(g.labelBackgroundColor="#EEEEEE")}else m="bottom"===this._position||"top"===this._position?0.9*this.chart.width>>0:0.9*this.chart.height>> +0,z=u(g.options.labelWrap)||g.labelWrap?"bottom"===this._position||"top"===this._position?0.8*this.chart.width>>0:0.8*this.chart.height>>0:1.5*this.labelFontSize,u(g.labelBackgroundColor)&&(u(g.startValue)&&0!==g.startValue?g.labelBackgroundColor=r?"transparent":null:g.labelBackgroundColor="#EEEEEE");a=new ka(this.ctx,{x:0,y:0,backgroundColor:g.labelBackgroundColor,borderColor:g.labelBorderColor,borderThickness:g.labelBorderThickness,cornerRadius:g.labelCornerRadius,maxWidth:g.options.labelMaxWidth? +g.options.labelMaxWidth:m,maxHeight:z,angle:this.labelAngle,text:g.labelFormatter?g.labelFormatter({chart:this.chart,axis:this,stripLine:g}):g.label,horizontalAlign:"left",fontSize:"outside"===g.labelPlacement?g.options.labelFontSize?g.labelFontSize:this.labelFontSize:g.labelFontSize,fontFamily:"outside"===g.labelPlacement?g.options.labelFontFamily?g.labelFontFamily:this.labelFontFamily:g.labelFontFamily,fontWeight:"outside"===g.labelPlacement?g.options.labelFontWeight?g.labelFontWeight:this.labelFontWeight: +g.labelFontWeight,fontColor:g.labelFontColor||g.color,fontStyle:"outside"===g.labelPlacement?g.options.labelFontStyle?g.labelFontStyle:this.fontWeight:g.labelFontStyle,textBaseline:"middle"});this._stripLineLabels.push({position:g.value,textBlock:a,effectiveHeight:null,stripLine:g})}};z.prototype.createLabelsAndCalculateWidth=function(){var a=0,d=0;this._labels=[];this._stripLineLabels=[];var b=this.chart.isNavigator?0:5;if("left"===this._position||"right"===this._position){this.createLabels();for(d= +0;d=this.viewportMinimum&&this._stripLineLabels[d].stripLine.value<=this.viewportMaximum)&& +(c=this._stripLineLabels[d].textBlock,e=c.measureText(),g=0===this.labelAngle?e.width:e.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+(e.height-c.fontSize/2)*Math.sin(Math.PI/180*Math.abs(this.labelAngle)),a=this.viewportMinimum&&this._stripLineLabels[b].stripLine.value<=this.viewportMaximum)&&(d=this._stripLineLabels[b].textBlock,e=d.measureText(),g=0===this.labelAngle?e.height:e.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(e.height-d.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)),an[f].viewportMaximum);v++)r[v].endValue=n[f].viewPortMinimum&&(n[f].scaleBreaks.lastBreakIndex=v));for(var z=v=0,t=0,C=0,x=0,D=0,y=0,A,E,F=l=0,H,I,L,r=H=I=L=!1,f=0;fv;){var G=0,R=0,S=0,U=0,W=e=0,K=0,$=0,V=0,X=0,P=0,ba=0;if(b&& +0p.width- +q?p.width-q:g.x2-ba-$);if(a&&0p.width-q?p.width-q:g.x2-ba-$),a[f]._labels&&1k&&(l+=0a[f].labelAngle?A-zk&&(l=E+t/2-k-ba),A-za[f].labelAngle&&0p.width-q?p.width-q:g.x2-ba-$),d[f].lineCoordinates.width=Math.abs(k-m),d[f]._labels&&1v;){V=U=R=S=$=K=W=e=Q=O=G=X=0;if(a&&0p.width-10?p.width-10:g.x2-V-W),b[f].labelAutoFit&&!u(C)&&(0b[f].labelAngle?Math.max(m,C):0===b[f].labelAngle? +Math.max(m,C/2):m),0c[f].chart.width-10?c[f].chart.width-10:g.x2-V-W),c[f]&& +c[f].labelAutoFit&&!u(D)&&(0b[f].chart.height-10?b[f].chart.height-10:g.y2),b[f].lineCoordinates.y1=l-(q[f]+b[f].margin+ +X),b[f].lineCoordinates.y2=l-(q[f]+b[f].margin+X),b[f].bounds={x1:m,y1:l-(q[f]+X+b[f].margin),x2:k,y2:h-(X+b[f].margin),width:k-m,height:q[f]},b[f].title&&(b[f]._titleTextBlock.maxWidth=0p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2-S):g.y2>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2;if(b&&0b[K].labelAngle?Math.max(k,C):0===b[K].labelAngle?Math.max(k,C/2):k,m=0>b[K].labelAngle||0===b[K].labelAngle?k-U:m);if(c&&0p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2-S):g.y2>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2;if(b&&0b[K].labelAngle?Math.max(k,C):0===b[K].labelAngle?Math.max(k,C/2):k,m=0>b[K].labelAngle||0=== +b[K].labelAngle?k-V:m);if(c&&0d[g].spacing?0:Math.abs(d[g].spacing/b),this.logarithmic&&(d[g].size=Math.pow(this.logarithmBase,d[g].size))};z.prototype.calculateBreaksInPixels=function(){if(!(this.scaleBreaks&&0>=this.scaleBreaks._appliedBreaks.length)){var a=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];a.length&&(this.scaleBreaks.firstBreakIndex=this.scaleBreaks.lastBreakIndex=null);for(var d=0;dthis.conversionParameters.maximum);d++)a[d].endValue< +this.conversionParameters.minimum||(u(this.scaleBreaks.firstBreakIndex)&&(this.scaleBreaks.firstBreakIndex=d),a[d].startValue>=this.conversionParameters.minimum&&(a[d].startPixel=this.convertValueToPixel(a[d].startValue),this.scaleBreaks.lastBreakIndex=d),a[d].endValue<=this.conversionParameters.maximum&&(a[d].endPixel=this.convertValueToPixel(a[d].endValue)))}};z.prototype.renderLabelsTicksAndTitle=function(){var a=this,d=!1,b=0,c=0,e=1,g=0;0!==this.labelAngle&&360!==this.labelAngle&&(e=1.2);if("undefined"=== +typeof this.options.interval){if("bottom"===this._position||"top"===this._position)if(this.logarithmic&&!this.equidistantInterval&&this.labelAutoFit){for(var b=[],e=0!==this.labelAngle&&360!==this.labelAngle?1:1.2,m,l=this.viewportMaximum,k=this.lineCoordinates.width/Math.log(this.range),h=this._labels.length-1;0<=h;h--){q=this._labels[h];if(q.positionthis.viewportMaximum||!(h===this._labels.length-1||mthis.lineCoordinates.width*e&&this.labelAutoFit&&(d=!0)}if("left"===this._position||"right"===this._position)if(this.logarithmic&& +!this.equidistantInterval&&this.labelAutoFit){for(var b=[],p,l=this.viewportMaximum,k=this.lineCoordinates.height/Math.log(this.range),h=this._labels.length-1;0<=h;h--){q=this._labels[h];if(q.positionthis.viewportMaximum||!(h===this._labels.length-1||pthis.lineCoordinates.height*e&&this.labelAutoFit&&(d=!0)}}this.logarithmic&&(!this.equidistantInterval&&this.labelAutoFit)&&this._labels.sort(function(a,b){return a.position-b.position});var h=0,q,n;if("bottom"===this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,n.y<<0),this.ctx.lineTo(c,n.y+this.tickLength<<0),this.ctx.stroke()),0===q.textBlock.angle?(n.x-=q.textBlock.width/2,n.y="inside"===this.labelPlacement? +n.y-(this.tickLength+q.textBlock.fontSize/2):n.y+this.tickLength+q.textBlock.fontSize/2):(n.x="inside"===this.labelPlacement?0>this.labelAngle?n.x:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):n.x-(0>this.labelAngle?q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0),n.y="inside"===this.labelPlacement?0>this.labelAngle?n.y-this.tickLength-5:n.y-this.tickLength-Math.abs(q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle)+5):n.y+this.tickLength+Math.abs(0>this.labelAngle?q.textBlock.width* +Math.sin(Math.PI/180*this.labelAngle)-5:5)),q.textBlock.x=n.x,q.textBlock.y=n.y);"inside"===this.labelPlacement&&this.chart.addEventListener("dataAnimationIterationEnd",function(){for(h=0;ha.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0;a.ctx.save(); +a.ctx.beginPath();a.ctx.moveTo(b,n.y<<0);a.ctx.lineTo(b,n.y-a.tickLength<<0);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.bounds.y2-this._titleTextBlock.height-3,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("top"===this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,n.y<<0),this.ctx.lineTo(c,n.y-this.tickLength<<0),this.ctx.stroke()),0===q.textBlock.angle?(n.x-=q.textBlock.width/2,n.y="inside"===this.labelPlacement? +n.y+this.labelFontSize/2+this.tickLength+5:n.y-(this.tickLength+q.textBlock.height-q.textBlock.fontSize/2)):(n.x="inside"===this.labelPlacement?0a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&& +(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(b,n.y<<0);a.ctx.lineTo(b,n.y+a.tickLength<<0);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.bounds.y1+1,this.titleMaxWidth= +this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("left"===this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0,this.ctx.beginPath(),this.ctx.moveTo(n.x<< +0,c),this.ctx.lineTo(n.x-this.tickLength<<0,c),this.ctx.stroke()),0===this.labelAngle?(q.textBlock.y=n.y,q.textBlock.x="inside"===this.labelPlacement?n.x+this.tickLength+5:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5):(q.textBlock.y="inside"===this.labelPlacement?n.y:n.y-q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle),q.textBlock.x="inside"===this.labelPlacement?n.x+this.tickLength+5:0a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness; +a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(n.x<<0,b);a.ctx.lineTo(n.x+a.tickLength<<0,b);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.bounds.x1+1,this._titleTextBlock.y=this.lineCoordinates.height/2+this._titleTextBlock.width/2+this.lineCoordinates.y1,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("right"=== +this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0,this.ctx.beginPath(),this.ctx.moveTo(n.x<<0,c),this.ctx.lineTo(n.x+this.tickLength<<0,c),this.ctx.stroke()),0===this.labelAngle? +(q.textBlock.y=n.y,q.textBlock.x="inside"===this.labelPlacement?n.x-q.textBlock.width-this.tickLength-5:n.x+this.tickLength+5):(q.textBlock.y="inside"===this.labelPlacement?n.y-q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle):0>this.labelAngle?n.y:n.y-(q.textBlock.height-q.textBlock.fontSize/2-5)*Math.cos(Math.PI/180*this.labelAngle),q.textBlock.x="inside"===this.labelPlacement?n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5:0a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.y<< +0)+0.5:n.y<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(n.x<<0,b);a.ctx.lineTo(n.x-a.tickLength<<0,b);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.bounds.x2-1,this._titleTextBlock.y=this.lineCoordinates.height/2-this._titleTextBlock.width/2+this.lineCoordinates.y1,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}g=0;if("inside"===this.labelPlacement)this.chart.addEventListener("dataAnimationIterationEnd", +function(){for(h=0;ha.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)||(a.ctx.save(),a.ctx.beginPath(),q.textBlock.render(!0),a.ctx.restore())},this);else for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||q.textBlock.render(!0)};z.prototype.renderInterlacedColors=function(){var a=this.chart.plotArea.ctx,d,b,c=this.chart.plotArea, +e=0;d=!0;if(("bottom"===this._position||"top"===this._position)&&this.interlacedColor)for(a.fillStyle=this.interlacedColor,e=0;ethis._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(Math.min(b.x,d.x),c.y1,Math.abs(b.x-d.x),Math.abs(c.y1-c.y2)),d=!1):d=!0;else if(("left"===this._position||"right"===this._position)&&this.interlacedColor)for(a.fillStyle= +this.interlacedColor,e=0;ethis._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(c.x1,Math.min(b.y,d.y),Math.abs(c.x1-c.x2),Math.abs(d.y-b.y)),d=!1):d=!0;a.beginPath()};z.prototype.renderStripLinesOfThicknessType=function(a){if(this.stripLines&&0this.viewportMaximum||u(h.value)||isNaN(this.range))||l.push(h))}for(c=0;cthis.viewportMaximum||isNaN(this.range))){a=this.getPixelCoordinatesOnAxis(b.position);if("outside"===b.stripLine.labelPlacement)if(h&&(this.ctx.strokeStyle= +h.color,"pixel"===h._thicknessType&&(this.ctx.lineWidth=h.thickness)),"bottom"===this._position){var p=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0;this.ctx.beginPath();this.ctx.moveTo(p,a.y<<0);this.ctx.lineTo(p,a.y+this.tickLength<<0);this.ctx.stroke();0===this.labelAngle?(a.x-=b.textBlock.width/2,a.y+=this.tickLength+b.textBlock.fontSize/2):(a.x-=0>this.labelAngle?b.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0,a.y+=this.tickLength+Math.abs(0>this.labelAngle?b.textBlock.width*Math.sin(Math.PI/ +180*this.labelAngle)-5:5))}else"top"===this._position?(p=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0,this.ctx.beginPath(),this.ctx.moveTo(p,a.y<<0),this.ctx.lineTo(p,a.y-this.tickLength<<0),this.ctx.stroke(),0===this.labelAngle?(a.x-=b.textBlock.width/2,a.y-=this.tickLength+b.textBlock.height):(a.x+=(b.textBlock.height-this.tickLength-this.labelFontSize/2)*Math.sin(Math.PI/180*this.labelAngle)-(0this.labelAngle?a.y:a.y-(b.textBlock.height-b.textBlock.fontSize/ +2-5)*Math.cos(Math.PI/180*this.labelAngle),a.x=0this.chart.plotArea.x1?u(h.startValue)?a.x-=b.textBlock.height-b.textBlock.fontSize/ +2:a.x-=b.textBlock.height/2-b.textBlock.fontSize/2+3:(b.textBlock.angle=90,u(h.startValue)?a.x+=b.textBlock.height-b.textBlock.fontSize/2:a.x+=b.textBlock.height/2-b.textBlock.fontSize/2+3),a.y=-90===b.textBlock.angle?"near"===b.stripLine.labelAlign?this.chart.plotArea.y2-3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1+b.textBlock.width)/2:this.chart.plotArea.y1+b.textBlock.width+3:"near"===b.stripLine.labelAlign?this.chart.plotArea.y2-b.textBlock.width-3:"center"=== +b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1-b.textBlock.width)/2:this.chart.plotArea.y1+3):"top"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.height-3,b.textBlock.measureText(),a.x-b.textBlock.height>this.chart.plotArea.x1?u(h.startValue)?a.x-=b.textBlock.height-b.textBlock.fontSize/2:a.x-=b.textBlock.height/2-b.textBlock.fontSize/2+3:(b.textBlock.angle=90,u(h.startValue)?a.x+= +b.textBlock.height-b.textBlock.fontSize/2:a.x+=b.textBlock.height/2-b.textBlock.fontSize/2+3),a.y=-90===b.textBlock.angle?"near"===b.stripLine.labelAlign?this.chart.plotArea.y1+b.textBlock.width+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1+b.textBlock.width)/2:this.chart.plotArea.y2-3:"near"===b.stripLine.labelAlign?this.chart.plotArea.y1+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1-b.textBlock.width)/2:this.chart.plotArea.y2- +b.textBlock.width-3):"left"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.width-3,b.textBlock.angle=0,b.textBlock.measureText(),a.y-b.textBlock.height>this.chart.plotArea.y1?u(h.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize+3:a.y-b.textBlock.heightthis.chart.plotArea.y1? +u(h.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize/2-3:a.y-b.textBlock.heightthis.viewportMaximum|| +isNaN(this.range))||a[d].render(this.maskCtx);this.maskCtx.restore()}};z.prototype.renderCrosshair=function(a,d){this.crosshair.render(a,d)};z.prototype.renderGrid=function(){if(this.gridThickness&&0this.viewportMaximum||this._labels[c].breaksLabelType)||(a.beginPath(),d=this.getPixelCoordinatesOnAxis(this._labels[c].position),d=1===a.lineWidth%2?(d.x<<0)+0.5:d.x<<0,a.moveTo(d,b.y1<<0),a.lineTo(d,b.y2<<0),a.stroke());else if("left"===this._position||"right"===this._position)for(var c=0;cthis.viewportMaximum||this._labels[c].breaksLabelType)||(a.beginPath(), +d=this.getPixelCoordinatesOnAxis(this._labels[c].position),d=1===a.lineWidth%2?(d.y<<0)+0.5:d.y<<0,a.moveTo(b.x1<<0,d),a.lineTo(b.x2<<0,d),a.stroke());a.restore()}};z.prototype.renderAxisLine=function(){var a=this.chart.ctx,d=r?this.chart._preRenderCtx:a,b=Math.ceil(this.tickThickness/(this.reversed?-2:2)),c=Math.ceil(this.tickThickness/(this.reversed?2:-2)),e,g;d.save();if("bottom"===this._position||"top"===this._position){if(this.lineThickness){this.reversed?(e=this.lineCoordinates.x2,g=this.lineCoordinates.x1): +(e=this.lineCoordinates.x1,g=this.lineCoordinates.x2);d.lineWidth=this.lineThickness;d.strokeStyle=this.lineColor?this.lineColor:"black";d.setLineDash&&d.setLineDash(R(this.lineDashType,this.lineThickness));var m=1===this.lineThickness%2?(this.lineCoordinates.y1<<0)+0.5:this.lineCoordinates.y1<<0;d.beginPath();if(this.scaleBreaks&&!u(this.scaleBreaks.firstBreakIndex))if(u(this.scaleBreaks.lastBreakIndex))e=this.scaleBreaks._appliedBreaks[this.scaleBreaks.firstBreakIndex].endPixel+c;else for(var l= +this.scaleBreaks.firstBreakIndex;l<=this.scaleBreaks.lastBreakIndex;l++)d.moveTo(e,m),d.lineTo(this.scaleBreaks._appliedBreaks[l].startPixel+b,m),e=this.scaleBreaks._appliedBreaks[l].endPixel+c;e&&(d.moveTo(e,m),d.lineTo(g,m));d.stroke()}}else if(("left"===this._position||"right"===this._position)&&this.lineThickness){this.reversed?(e=this.lineCoordinates.y1,g=this.lineCoordinates.y2):(e=this.lineCoordinates.y2,g=this.lineCoordinates.y1);d.lineWidth=this.lineThickness;d.strokeStyle=this.lineColor; +d.setLineDash&&d.setLineDash(R(this.lineDashType,this.lineThickness));m=1===this.lineThickness%2?(this.lineCoordinates.x1<<0)+0.5:this.lineCoordinates.x1<<0;d.beginPath();if(this.scaleBreaks&&!u(this.scaleBreaks.firstBreakIndex))if(u(this.scaleBreaks.lastBreakIndex))e=this.scaleBreaks._appliedBreaks[this.scaleBreaks.firstBreakIndex].endPixel+b;else for(l=this.scaleBreaks.firstBreakIndex;l<=this.scaleBreaks.lastBreakIndex;l++)d.moveTo(m,e),d.lineTo(m,this.scaleBreaks._appliedBreaks[l].startPixel+c), +e=this.scaleBreaks._appliedBreaks[l].endPixel+b;e&&(d.moveTo(m,e),d.lineTo(m,g));d.stroke()}r&&(a.drawImage(this.chart._preRenderCanvas,0,0,this.chart.width,this.chart.height),this.chart._breaksCanvasCtx&&this.chart._breaksCanvasCtx.drawImage(this.chart._preRenderCanvas,0,0,this.chart.width,this.chart.height),d.clearRect(0,0,this.chart.width,this.chart.height));d.restore()};z.prototype.getPixelCoordinatesOnAxis=function(a){var d={};if("bottom"===this._position||"top"===this._position)d.x=this.convertValueToPixel(a), +d.y=this.lineCoordinates.y1;if("left"===this._position||"right"===this._position)d.y=this.convertValueToPixel(a),d.x=this.lineCoordinates.x2;return d};z.prototype.convertPixelToValue=function(a){if("undefined"===typeof a)return null;var d=0,b=0,c,d=!0,e=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[],b="number"===typeof a?a:"left"===this._position||"right"===this._position?a.y:a.x;if(this.logarithmic){a=c=Math.pow(this.logarithmBase,(b-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit); +if(b<=this.conversionParameters.reference===("left"===this._position||"right"===this._position)!==this.reversed)for(b=0;be[b].startValue/this.conversionParameters.minimum){c/=e[b].startValue/this.conversionParameters.minimum;if(ce[b].startValue/e[b-1].endValue){c/=e[b].startValue/e[b-1].endValue;if(cthis.conversionParameters.minimum))if(d)if(e[b].endValue>this.conversionParameters.minimum){if(1e[b].startValue){a=Math.pow(e[b].endValue/e[b].startValue,Math.log(c)/Math.log(e[b].size));break}else a*=e[b].startValue/this.conversionParameters.minimum*Math.pow(e[b].size,Math.log(e[b].startValue/this.conversionParameters.minimum)/Math.log(e[b].endValue/e[b].startValue))*c,c*=Math.pow(e[b].size,Math.log(this.conversionParameters.minimum/e[b].startValue)/Math.log(e[b].endValue/e[b].startValue));d=!1}else if(c1/e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1>=e[b].size?1:Math.log(c)/Math.log(e[b].size))*c;break}else a/=e[b].endValue/e[b].startValue/e[b].size;c*=e[b].size;d=!1}else break;else if(c1/e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1>=e[b].size?1:Math.log(c)/Math.log(e[b].size))*c;break}else a/=e[b].endValue/e[b].startValue/e[b].size;c*=e[b].size}else break; +d=a*this.viewportMinimum}else{a=c=(b-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit;if(b<=this.conversionParameters.reference===("left"===this._position||"right"===this._position)!==this.reversed)for(b=0;b=e[b].size?0:c*(e[b].endValue- +e[b].startValue)/e[b].size;break}else a+=e[b].endValue-this.conversionParameters.minimum-e[b].size*(e[b].endValue-this.conversionParameters.minimum)/(e[b].endValue-e[b].startValue),c-=e[b].size*(e[b].endValue-this.conversionParameters.minimum)/(e[b].endValue-e[b].startValue);d=!1}else if(c>e[b].startValue-this.conversionParameters.minimum){c-=e[b].startValue-this.conversionParameters.minimum;if(ce[b].startValue-e[b-1].endValue){c-=e[b].startValue-e[b-1].endValue;if(cthis.conversionParameters.minimum))if(d)if(e[b].endValue>this.conversionParameters.minimum)if(e[b].size&&this.conversionParameters.minimum+c*(e[b].endValue- +e[b].startValue)/e[b].size>e[b].startValue){a=0>=e[b].size?0:c*(e[b].endValue-e[b].startValue)/e[b].size;break}else a+=e[b].startValue-this.conversionParameters.minimum+e[b].size*(this.conversionParameters.minimum-e[b].startValue)/(e[b].endValue-e[b].startValue),c+=e[b].size*(this.conversionParameters.minimum-e[b].startValue)/(e[b].endValue-e[b].startValue),d=!1;else if(c-1*e[b].size){a+=(e[b].endValue- +e[b].startValue)*(0===e[b].size?1:c/e[b].size)+c;break}else a-=e[b].endValue-e[b].startValue-e[b].size;c+=e[b].size;d=!1}else break;else if(c-1*e[b].size){a+=(e[b].endValue-e[b].startValue)*(0===e[b].size?1:c/e[b].size)+c;break}else a-=e[b].endValue-e[b].startValue-e[b].size;c+=e[b].size}else break;d=this.conversionParameters.minimum+a}return d};z.prototype.convertValueToPixel=function(a){a=this.getApparentDifference(this.conversionParameters.minimum, +a,a);return this.logarithmic?this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*Math.log(a/this.conversionParameters.minimum)/this.conversionParameters.lnLogarithmBase+0.5<<0:"axisX"===this.type?this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5<<0:this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5};z.prototype.getApparentDifference=function(a, +d,b,c){var e=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];if(this.logarithmic){b=u(b)?d/a:b;for(var g=0;ge[g].endValue||(a<=e[g].startValue&&d>=e[g].endValue?b=b/e[g].endValue*e[g].startValue*e[g].size:a>=e[g].startValue&&d>=e[g].endValue?b=b/e[g].endValue*a*Math.pow(e[g].size,Math.log(e[g].endValue/a)/Math.log(e[g].endValue/e[g].startValue)):a<=e[g].startValue&&d<=e[g].endValue?b=b/d*e[g].startValue*Math.pow(e[g].size,Math.log(d/e[g].startValue)/Math.log(e[g].endValue/ +e[g].startValue)):!c&&(a>e[g].startValue&&de[g].endValue||(a<=e[g].startValue&&d>=e[g].endValue?b=b-e[g].endValue+e[g].startValue+e[g].size:a>e[g].startValue&&d>=e[g].endValue?b=b-e[g].endValue+a+e[g].size*(e[g].endValue-a)/(e[g].endValue-e[g].startValue):a<=e[g].startValue&&de[g].startValue&&da[e].endValue||(this.viewportMinimum>=a[e].startValue&&this.viewportMaximum<= +a[e].endValue?b=0:this.viewportMinimum<=a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c/a[e].endValue*a[e].startValue,b=0a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c/a[e].endValue*this.viewportMinimum,b=0a[e].endValue||(this.viewportMinimum>=a[e].startValue&&this.viewportMaximum<=a[e].endValue?b=0:this.viewportMinimum<=a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c-a[e].endValue+a[e].startValue,b=0a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c-a[e].endValue+this.viewportMinimum, +b=0this.maxWidth?8:6);var a=Math.max(c,Math.floor(this.maxWidth/a)),e,g,m,c=0;!u(this.options.viewportMinimum)&&(!u(this.options.viewportMaximum)&&this.options.viewportMinimum>=this.options.viewportMaximum)&& +(this.viewportMinimum=this.viewportMaximum=null);if(u(this.options.viewportMinimum)&&!u(this.sessionVariables.newViewportMinimum)&&!isNaN(this.sessionVariables.newViewportMinimum))this.viewportMinimum=this.sessionVariables.newViewportMinimum;else if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=this.minimum;if(u(this.options.viewportMaximum)&&!u(this.sessionVariables.newViewportMaximum)&&!isNaN(this.sessionVariables.newViewportMaximum))this.viewportMaximum=this.sessionVariables.newViewportMaximum; +else if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;if(this.scaleBreaks)for(c=0;c=this.scaleBreaks._appliedBreaks[c].startValue||!u(this.options.minimum)&&this.options.minimum>=this.scaleBreaks._appliedBreaks[c].startValue||!u(this.options.viewportMinimum)&&this.viewportMinimum>=this.scaleBreaks._appliedBreaks[c].startValue)&& +(!u(this.sessionVariables.newViewportMaximum)&&this.sessionVariables.newViewportMaximum<=this.scaleBreaks._appliedBreaks[c].endValue||!u(this.options.maximum)&&this.options.maximum<=this.scaleBreaks._appliedBreaks[c].endValue||!u(this.options.viewportMaximum)&&this.viewportMaximum<=this.scaleBreaks._appliedBreaks[c].endValue)){this.scaleBreaks._appliedBreaks.splice(c,1);break}if("axisX"===this.type){if(this.dataSeries&&0g?(c=Math.min(0.01*Math.abs(this.getApparentDifference(g,e,null,!0)),5),0<=g?e=g-c:g=isFinite(e)?e+c:0):(c=Math.min(0.01*Math.abs(this.getApparentDifference(e,g, +null,!0)),0.05),0!==g&&(g+=c),0!==e&&(e-=c)),m=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1g&&(g=0));c=this.getApparentDifference(isNaN(this.viewportMinimum)||null===this.viewportMinimum?e:this.viewportMinimum,isNaN(this.viewportMaximum)||null===this.viewportMaximum?g:this.viewportMaximum,null, +!0);if("axisX"===this.type&&b){this.intervalType||(c/1<=a?(this.interval=1,this.intervalType="millisecond"):c/2<=a?(this.interval=2,this.intervalType="millisecond"):c/5<=a?(this.interval=5,this.intervalType="millisecond"):c/10<=a?(this.interval=10,this.intervalType="millisecond"):c/20<=a?(this.interval=20,this.intervalType="millisecond"):c/50<=a?(this.interval=50,this.intervalType="millisecond"):c/100<=a?(this.interval=100,this.intervalType="millisecond"):c/200<=a?(this.interval=200,this.intervalType= +"millisecond"):c/250<=a?(this.interval=250,this.intervalType="millisecond"):c/300<=a?(this.interval=300,this.intervalType="millisecond"):c/400<=a?(this.interval=400,this.intervalType="millisecond"):c/500<=a?(this.interval=500,this.intervalType="millisecond"):c/(1*S.secondDuration)<=a?(this.interval=1,this.intervalType="second"):c/(2*S.secondDuration)<=a?(this.interval=2,this.intervalType="second"):c/(5*S.secondDuration)<=a?(this.interval=5,this.intervalType="second"):c/(10*S.secondDuration)<=a?(this.interval= +10,this.intervalType="second"):c/(15*S.secondDuration)<=a?(this.interval=15,this.intervalType="second"):c/(20*S.secondDuration)<=a?(this.interval=20,this.intervalType="second"):c/(30*S.secondDuration)<=a?(this.interval=30,this.intervalType="second"):c/(1*S.minuteDuration)<=a?(this.interval=1,this.intervalType="minute"):c/(2*S.minuteDuration)<=a?(this.interval=2,this.intervalType="minute"):c/(5*S.minuteDuration)<=a?(this.interval=5,this.intervalType="minute"):c/(10*S.minuteDuration)<=a?(this.interval= +10,this.intervalType="minute"):c/(15*S.minuteDuration)<=a?(this.interval=15,this.intervalType="minute"):c/(20*S.minuteDuration)<=a?(this.interval=20,this.intervalType="minute"):c/(30*S.minuteDuration)<=a?(this.interval=30,this.intervalType="minute"):c/(1*S.hourDuration)<=a?(this.interval=1,this.intervalType="hour"):c/(2*S.hourDuration)<=a?(this.interval=2,this.intervalType="hour"):c/(3*S.hourDuration)<=a?(this.interval=3,this.intervalType="hour"):c/(6*S.hourDuration)<=a?(this.interval=6,this.intervalType= +"hour"):c/(1*S.dayDuration)<=a?(this.interval=1,this.intervalType="day"):c/(2*S.dayDuration)<=a?(this.interval=2,this.intervalType="day"):c/(4*S.dayDuration)<=a?(this.interval=4,this.intervalType="day"):c/(1*S.weekDuration)<=a?(this.interval=1,this.intervalType="week"):c/(2*S.weekDuration)<=a?(this.interval=2,this.intervalType="week"):c/(3*S.weekDuration)<=a?(this.interval=3,this.intervalType="week"):c/(1*S.monthDuration)<=a?(this.interval=1,this.intervalType="month"):c/(2*S.monthDuration)<=a?(this.interval= +2,this.intervalType="month"):c/(3*S.monthDuration)<=a?(this.interval=3,this.intervalType="month"):c/(6*S.monthDuration)<=a?(this.interval=6,this.intervalType="month"):(this.interval=c/(1*S.yearDuration)<=a?1:c/(2*S.yearDuration)<=a?2:c/(4*S.yearDuration)<=a?4:Math.floor(z.getNiceNumber(c/(a-1),!0)/S.yearDuration),this.intervalType="year"));if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=e-m/2;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum= +g+m/2;d?this.autoValueFormatString="MMM DD YYYY HH:mm":"year"===this.intervalType?this.autoValueFormatString="YYYY":"month"===this.intervalType?this.autoValueFormatString="MMM YYYY":"week"===this.intervalType?this.autoValueFormatString="MMM DD YYYY":"day"===this.intervalType?this.autoValueFormatString="MMM DD YYYY":"hour"===this.intervalType?this.autoValueFormatString="hh:mm TT":"minute"===this.intervalType?this.autoValueFormatString="hh:mm TT":"second"===this.intervalType?this.autoValueFormatString= +"hh:mm:ss TT":"millisecond"===this.intervalType&&(this.autoValueFormatString="fff'ms'");this.valueFormatString||(this.valueFormatString=this.autoValueFormatString)}else{this.intervalType="number";c=z.getNiceNumber(c,!1);this.interval=this.options&&0g?(c=Math.min(0.01*Math.abs(this.getApparentDifference(g,e,null,!0)),5),0<=g?e=g-c:g=isFinite(e)?e+c:0):(c=Math.min(0.01*Math.abs(this.getApparentDifference(e,g,null,!0)),0.05),0!==g&&(g+=c),0!==e&&(e-=c)):(g="undefined"===typeof this.options.interval?-Infinity:this.options.interval,e="undefined"!==typeof this.options.interval||isFinite(this.dataInfo.minDiff)?0:Infinity),m=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1g&&(g=0)),Math.abs(this.getApparentDifference(e,g,null,!0)),"axisX"===this.type&&b){this.valueType="dateTime";if(null===this.minimum||isNaN(this.minimum))this.minimum=e-m/2;if(null===this.maximum||isNaN(this.maximum))this.maximum=g+m/2}else this.intervalType=this.valueType="number",null===this.minimum&&(this.minimum="axisX"===this.type?e-m/2:Math.floor(e/this.interval)*this.interval,this.minimum=Math.min(this.minimum, +null===this.sessionVariables.viewportMinimum||isNaN(this.sessionVariables.viewportMinimum)?Infinity:this.sessionVariables.viewportMinimum)),null===this.maximum&&(this.maximum="axisX"===this.type?g+m/2:Math.ceil(g/this.interval)*this.interval,this.maximum=Math.max(this.maximum,null===this.sessionVariables.viewportMaximum||isNaN(this.sessionVariables.viewportMaximum)?-Infinity:this.sessionVariables.viewportMaximum)),0===this.maximum&&0===this.minimum&&(0===this.options.minimum?this.maximum+=10:0=== +this.options.maximum&&(this.minimum-=10));u(this.sessionVariables.newViewportMinimum)&&(this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum));u(this.sessionVariables.newViewportMaximum)&&(this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum));this.range=this.viewportMaximum-this.viewportMinimum;this.intervalStartPosition="axisX"===this.type&&b?this.getLabelStartPoint(new Date(this.viewportMinimum),this.intervalType,this.interval):Math.floor((this.viewportMinimum+0.2*this.interval)/ +this.interval)*this.interval;this.valueFormatString||(this.valueFormatString=z.generateValueFormatString(this.range,2))}};z.prototype.calculateLogarithmicAxisParameters=function(){var a=this.chart.layoutManager.getFreeSpace(),d=Math.log(this.logarithmBase),b;"bottom"===this._position||"top"===this._position?(this.maxWidth=a.width,this.maxHeight=a.height):(this.maxWidth=a.height,this.maxHeight=a.width);var a="axisX"===this.type?500>this.maxWidth?7:Math.max(7,Math.floor(this.maxWidth/100)):Math.max(Math.floor(this.maxWidth/ +50),3),c,e,g,m;m=1;if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=this.minimum;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;if(this.scaleBreaks)for(m=0;m=this.scaleBreaks._appliedBreaks[m].startValue||!u(this.options.minimum)&&this.options.minimum>=this.scaleBreaks._appliedBreaks[m].startValue|| +!u(this.options.viewportMinimum)&&this.viewportMinimum>=this.scaleBreaks._appliedBreaks[m].startValue)&&(!u(this.sessionVariables.newViewportMaximum)&&this.sessionVariables.newViewportMaximum<=this.scaleBreaks._appliedBreaks[m].endValue||!u(this.options.maximum)&&this.options.maximum<=this.scaleBreaks._appliedBreaks[m].endValue||!u(this.options.viewportMaximum)&&this.viewportMaximum<=this.scaleBreaks._appliedBreaks[m].endValue)){this.scaleBreaks._appliedBreaks.splice(m,1);break}"axisX"===this.type? +(c=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,e=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,1===e/c&&(m=Math.pow(this.logarithmBase,"undefined"===typeof this.options.interval?0.4:this.options.interval),e*=m,c/=m),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(c=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin, +e=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,0>=c&&!isFinite(e)?(e="undefined"===typeof this.options.interval?0:this.options.interval,c=1):0>=c?c=e:isFinite(e)||(e=c),1===c&&1===e?(e*=this.logarithmBase-1/this.logarithmBase,c=1):1===e/c?(m=Math.min(e*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),e*=m,c/=m):c>e?(m=Math.min(c/e*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),1<=e?c=e/m:e=c*m):(m=Math.min(e/c*Math.pow(this.logarithmBase, +0.01),Math.pow(this.logarithmBase,0.04)),1!==e&&(e*=m),1!==c&&(c/=m)),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.viewportMinimum||isNaN(this.viewportMinimum))&&1e&&(e=1));m=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?e:this.viewportMaximum)/(isNaN(this.viewportMinimum)||null=== +this.viewportMinimum?c:this.viewportMinimum);var l=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?e:this.viewportMaximum)-(isNaN(this.viewportMinimum)||null===this.viewportMinimum?c:this.viewportMinimum);this.intervalType="number";m=Math.pow(this.logarithmBase,z.getNiceNumber(Math.abs(Math.log(m)/d),!1));this.options&&0this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(c=null!==this.minimum?this.minimum:this.dataInfo.min,e=null!==this.maximum?this.maximum:this.dataInfo.max,isFinite(c)||isFinite(e)?1===c&&1===e?(e*=this.logarithmBase,c/=this.logarithmBase):1===e/c?(m=Math.pow(this.logarithmBase,this.interval),e*=m,c/=m):c>e?(m= +Math.min(0.01*(c/e),5),1<=e?c=e/m:e=c*m):(m=Math.min(e/c*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,0.04)),1!==e&&(e*=m),1!==c&&(c/=m)):(e="undefined"===typeof this.options.interval?0:this.options.interval,c=1),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.minimum||isNaN(this.minimum))&&1e&&(e=1)),this.intervalType="number",null===this.minimum&&(this.minimum="axisX"===this.type?c/Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.floor(Math.log(c)/d/this.interval)),this.minimum=Math.min(this.minimum,null===this.sessionVariables.viewportMinimum||isNaN(this.sessionVariables.viewportMinimum)?"undefined"===typeof this.sessionVariables.newViewportMinimum?Infinity:this.sessionVariables.newViewportMinimum:this.sessionVariables.viewportMinimum)),null===this.maximum&&(this.maximum= +"axisX"===this.type?e*Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.ceil(Math.log(e)/d/this.interval)),this.maximum=Math.max(this.maximum,null===this.sessionVariables.viewportMaximum||isNaN(this.sessionVariables.viewportMaximum)?"undefined"===typeof this.sessionVariables.newViewportMaximum?0:this.sessionVariables.newViewportMaximum:this.sessionVariables.viewportMaximum)),1===this.maximum&&1===this.minimum&&(1===this.options.minimum?this.maximum*=this.logarithmBase-1/this.logarithmBase: +1===this.options.maximum&&(this.minimum/=this.logarithmBase-1/this.logarithmBase));this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum);this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum);this.viewportMinimum>this.viewportMaximum&&(!this.options.viewportMinimum&&!this.options.minimum||this.options.viewportMaximum||this.options.maximum?this.options.viewportMinimum||this.options.minimum||!this.options.viewportMaximum&&!this.options.maximum||(this.viewportMinimum=this.minimum= +(this.options.viewportMaximum||this.options.maximum)/Math.pow(this.logarithmBase,2*Math.ceil(this.interval))):this.viewportMaximum=this.maximum=this.options.viewportMinimum||this.options.minimum);c=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(d*this.interval)+0.2)*this.interval);this.range=this.viewportMaximum/this.viewportMinimum;this.noTicks=a;if(!this.options.interval&&this.rangethis.viewportMaximum||3>a?2:3)){for(d=Math.floor(this.viewportMinimum/ +b+0.5)*b;dthis.interval&&(this.interval=b,c=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(d*this.interval)+0.2)*this.interval))),this.equidistantInterval=!0,this.intervalStartPosition=c;if(!this.valueFormatString&&(this.valueFormatString="#,##0.##",1>this.viewportMinimum)){d=Math.floor(Math.abs(Math.log(this.viewportMinimum)/ +Math.LN10))+2;if(isNaN(d)||!isFinite(d))d=2;if(2a&&(c+=Math.floor(Math.abs(Math.log(a)/Math.LN10)),isNaN(c)||!isFinite(c))&&(c=d);for(var e=0;eb?1>=c?1:5>=c?5:10:Math.max(Math.floor(c),1);return-20>b?Number(c*Math.pow(10,b)):Number((c*Math.pow(10,b)).toFixed(20))};z.getNiceNumber= +function(a,d){var b=Math.floor(Math.log(a)/Math.LN10),c=a/Math.pow(10,b),c=d?1.5>c?1:3>c?2:7>c?5:10:1>=c?1:2>=c?2:5>=c?5:10;return-20>b?Number(c*Math.pow(10,b)):Number((c*Math.pow(10,b)).toFixed(20))};z.prototype.getLabelStartPoint=function(){var a=S[this.intervalType+"Duration"]*this.interval,a=new Date(Math.floor(this.viewportMinimum/a)*a);if("millisecond"!==this.intervalType)if("second"===this.intervalType)0=a||"bottom"===this.scaleBreaks.parent._position&&0<=a)this.ctx.lineTo(c,l),this.ctx.lineTo(m,l),this.ctx.lineTo(m,e);else if("wavy"===this.type){k=c;h=e;g=0.5;p=(l-h)/a/3;for(var n=0;n=a||"right"===this.scaleBreaks.parent._position&&0<=a)this.ctx.lineTo(m,e),this.ctx.lineTo(m,l), +this.ctx.lineTo(c,l);else if("wavy"===this.type){k=c;h=e;g=0.5;p=(m-k)/a/3;for(n=0;nthis.parent.range?2:Math.floor(Math.abs(Math.log(this.parent.range)/Math.LN10))+(5>this.parent.range?2:10>this.parent.range? +1:0):50this.parent.range?2:10>this.parent.range?1:0);this.valueFormatString=z.generateValueFormatString(this.parent.range,h)}var l=null===this.opacity?1:this.opacity,h=Math.abs("pixel"===this._thicknessType?this.thickness:this.parent.conversionParameters.pixelPerUnit*this.thickness),p=this.chart.overlaidCanvasCtx,q=p.globalAlpha;p.globalAlpha=l;p.beginPath();p.strokeStyle=this.color;p.lineWidth=h;p.save();this.labelFontSize= +u(this.options.labelFontSize)?this.parent.labelFontSize:this.labelFontSize;if("left"===this.parent._position||"right"===this.parent._position)this.labelMaxWidth=u(this.options.labelMaxWidth)?this.parent.bounds.x2-this.parent.bounds.x1:this.labelMaxWidth,this.labelMaxHeight=u(this.options.labelWrap)||this.labelWrap?3*this.chart.height:2*this.labelFontSize;else if("top"===this.parent._position||"bottom"===this.parent._position)this.labelMaxWidth=u(this.options.labelMaxWidth)?3*this.chart.width:this.labelMaxWidth, +this.labelMaxHeight=u(this.options.labelWrap)||this.labelWrap?this.parent.bounds.height:2*this.labelFontSize;0this.chart.bounds.x2?l.x=this.chart.bounds.x2-l.width:l.xthis.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}}else if("bottom"===this.parent._position||"top"===this.parent._position){n=this.parent.convertPixelToValue({x:a});for(r=0;rthis.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width);l.xthis.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"=== +this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}m=null;("bottom"===this.parent._position||"top"===this.parent._position)&&(b>=this.parent.convertValueToPixel(this.parent.viewportMinimum)&&c<=this.parent.convertValueToPixel(this.parent.viewportMaximum))&&(0=this.parent.convertValueToPixel(this.parent.viewportMaximum)&& +e<=this.parent.convertValueToPixel(this.parent.viewportMinimum))&&(0this.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x1-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}else{if("bottom"===this.parent._position||"top"===this.parent._position)l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(a)}):u(this.options.label)? +ba(this.parent.convertPixelToValue(a),this.valueFormatString,this.chart._cultureInfo):this.label,l.x=b-l.measureText().width/2,l.x+l.width>this.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width),l.xthis.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width);l.xthis.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2),"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2);0(new Date).getTime()-this._lastUpdated||(this._lastUpdated=(new Date).getTime(), +this.chart.resetOverlayedCanvas(),this._updateToolTip(a,d))};$.prototype._updateToolTip=function(a,d,b){b="undefined"===typeof b?!0:b;this.container||this._initialize();this.enabled||this.hide();if(!this.chart.disableToolTip){if("undefined"===typeof a||"undefined"===typeof d){if(isNaN(this._prevX)||isNaN(this._prevY))return;a=this._prevX;d=this._prevY}else this._prevX=a,this._prevY=d;var c=null,e=null,g=[],k=0;if(this.shared&&this.enabled&&"none"!==this.chart.plotInfo.axisPlacement){if("xySwapped"=== +this.chart.plotInfo.axisPlacement){var l=[];if(this.chart.axisX)for(var p=0;ph.dataSeries.axisY.viewportMaximum&&b++;b-h.dataPoint.y.length&&g.push(h)}else"column"===e.type||"bar"===e.type?0>h.dataPoint.y?0>h.dataSeries.axisY.viewportMinimum&&h.dataSeries.axisY.viewportMaximum>=h.dataPoint.y&&g.push(h):h.dataSeries.axisY.viewportMinimum<=h.dataPoint.y&&0<=h.dataSeries.axisY.viewportMaximum&&g.push(h):"bubble"===e.type?(b=this.chart._eventManager.objectMap[e.dataPointIds[h.index]].size/2,h.dataPoint.y>= +h.dataSeries.axisY.viewportMinimum-b&&h.dataPoint.y<=h.dataSeries.axisY.viewportMaximum+b&&g.push(h)):"waterfall"===e.type?(b=0,h.cumulativeSumYStartValueh.dataSeries.axisY.viewportMaximum&&b++,h.cumulativeSumh.dataSeries.axisY.viewportMaximum&&b++,2>b&&-2=h.dataSeries.axisY.viewportMinimum&& +h.dataPoint.y<=h.dataSeries.axisY.viewportMaximum)&&g.push(h);else g.push(h)}}if(0a&&(a+=this.container.clientWidth+20);a+this.container.clientWidth> +Math.max(this.chart.container.clientWidth,this.chart.width)&&(a=Math.max(0,Math.max(this.chart.container.clientWidth,this.chart.width)-this.container.clientWidth));d=1!==g.length||this.shared||"line"!==g[0].dataSeries.type&&"stepLine"!==g[0].dataSeries.type&&"spline"!==g[0].dataSeries.type&&"area"!==g[0].dataSeries.type&&"stepArea"!==g[0].dataSeries.type&&"splineArea"!==g[0].dataSeries.type?"bar"===g[0].dataSeries.type||"rangeBar"===g[0].dataSeries.type||"stackedBar"===g[0].dataSeries.type||"stackedBar100"=== +g[0].dataSeries.type?g[0].dataSeries.axisX.convertValueToPixel(g[0].dataPoint.x):d:g[0].dataSeries.axisY.convertValueToPixel(g[0].dataPoint.y);d=-d+10;0":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content? +this.content:"{name}:  {y}",p=b.axisXIndex):"bubble"===b.type?(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:  {y},   {z}"): +"rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"error"===b.type?(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:  {y[0]}, {y[1]}"):"candlestick"=== +b.type||"ohlc"===b.type?(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:
Open:   {y[0]}
High:    {y[1]}
Low:   {y[2]}
Close:   {y[3]}"):"boxAndWhisker"=== +b.type&&(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:
Minimum:   {y[0]}
Q1:               {y[1]}
Q2:               {y[4]}
Q3:               {y[2]}
Maximum:  {y[3]}"), +null===d&&(d=""),!0===this.reversed?(d=this.chart.replaceKeywordsWithValue(g,c,b,e)+d,l"+d)):(d+=this.chart.replaceKeywordsWithValue(g,c,b,e),l")));null!==d&&(d=h+d)}else{b=a[0].dataSeries;c=a[0].dataPoint;e=a[0].index;if(null===c.toolTipContent||"undefined"===typeof c.toolTipContent&&null===b.options.toolTipContent)return null;"line"===b.type||"stepLine"===b.type||"spline"===b.type||"area"===b.type||"stepArea"===b.type||"splineArea"===b.type||"column"=== +b.type||"bar"===b.type||"scatter"===b.type||"stackedColumn"===b.type||"stackedColumn100"===b.type||"stackedBar"===b.type||"stackedBar100"===b.type||"stackedArea"===b.type||"stackedArea100"===b.type||"waterfall"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+":  {y}":"bubble"===b.type?g=c.toolTipContent? +c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+":  {y},   {z}":"pie"===b.type||"doughnut"===b.type||"funnel"===b.type||"pyramid"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.name?"{name}:  ":c.label?"{label}:  ":"")+"{y}":"rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"error"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+" :  {y[0]},  {y[1]}": +"candlestick"===b.type||"ohlc"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+"
Open:   {y[0]}
High:    {y[1]}
Low:     {y[2]}
Close:   {y[3]}":"boxAndWhisker"===b.type&&(g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent: +this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+"
Minimum:   {y[0]}
Q1:               {y[1]}
Q2:               {y[4]}
Q3:               {y[2]}
Maximum:  {y[3]}"); +null===d&&(d="");d+=this.chart.replaceKeywordsWithValue(g,c,b,e)}return d};$.prototype.enableAnimation=function(){if(!this.container.style.WebkitTransition){var a=this.getContainerTransition(this.containerTransitionDuration);this.container.style.WebkitTransition=a;this.container.style.MsTransition=a;this.container.style.transition=a;this.container.style.MozTransition=this.mozContainerTransition}};$.prototype.disableAnimation=function(){this.container.style.WebkitTransition&&(this.container.style.WebkitTransition= +"",this.container.style.MozTransition="",this.container.style.MsTransition="",this.container.style.transition="")};$.prototype.hide=function(a){this.container&&(this.container.style.display="none",this.currentSeriesIndex=-1,this._prevY=this._prevX=NaN,("undefined"===typeof a||a)&&this.chart.resetOverlayedCanvas())};$.prototype.show=function(a,d,b){this._updateToolTip(a,d,"undefined"===typeof b?!1:b)};$.prototype.fixMozTransitionDelay=function(a,d){if(20c&&a.push(d),d.animationCallback(c),1<=c&&d.onComplete)d.onComplete();this.animations=a;0g;g++)for(var e=0;3>e;e++){for(var f=0,d=0;3>d;d++)f+=a[g][d]*b[d][e];c[g][e]=f}return c}function P(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX= +a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.globalAlpha=a.globalAlpha;b.font=a.font;b.textAlign=a.textAlign;b.textBaseline=a.textBaseline;b.arcScaleX_=a.arcScaleX_;b.arcScaleY_=a.arcScaleY_;b.lineScale_=a.lineScale_}function Q(a){var b=a.indexOf("(",3),c=a.indexOf(")",b+1),b=a.substring(b+1,c).split(",");if(4!=b.length||"a"!=a.charAt(3))b[3]=1;return b}function E(a,b,c){return Math.min(c,Math.max(b,a))}function F(a,b,c){0>c&&c++;16*c?a+6*(b-a)*c: +1>2*c?b:2>3*c?a+6*(b-a)*(2/3-c):a}function G(a){if(a in H)return H[a];var b,c=1;a=String(a);if("#"==a.charAt(0))b=a;else if(/^rgb/.test(a)){c=Q(a);b="#";for(var g,e=0;3>e;e++)g=-1!=c[e].indexOf("%")?Math.floor(255*(parseFloat(c[e])/100)):+c[e],b+=v[E(g,0,255)];c=+c[3]}else if(/^hsl/.test(a)){e=c=Q(a);b=parseFloat(e[0])/360%360;0>b&&b++;g=E(parseFloat(e[1])/100,0,1);e=E(parseFloat(e[2])/100,0,1);if(0==g)g=e=b=e;else{var f=0.5>e?e*(1+g):e+g-e*g,d=2*e-f;g=F(d,f,b+1/3);e=F(d,f,b);b=F(d,f,b-1/3)}b="#"+ +v[Math.floor(255*g)]+v[Math.floor(255*e)]+v[Math.floor(255*b)];c=c[3]}else b=Z[a]||a;return H[a]={color:b,alpha:c}}function C(a){this.m_=D();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=1*q;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=a;var b="width:"+a.clientWidth+"px;height:"+a.clientHeight+"px;overflow:hidden;position:absolute", +c=a.ownerDocument.createElement("div");c.style.cssText=b;a.appendChild(c);b=c.cloneNode(!1);b.style.backgroundColor="red";b.style.filter="alpha(opacity=0)";a.appendChild(b);this.element_=c;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}function R(a,b,c,g){a.currentPath_.push({type:"bezierCurveTo",cp1x:b.x,cp1y:b.y,cp2x:c.x,cp2y:c.y,x:g.x,y:g.y});a.currentX_=g.x;a.currentY_=g.y}function S(a,b){var c=G(a.strokeStyle),g=c.color,c=c.alpha*a.globalAlpha,e=a.lineScale_*a.lineWidth;1>e&&(c*=e);b.push("')}function T(a,b,c,g){var e=a.fillStyle,f=a.arcScaleX_,d=a.arcScaleY_,k=g.x-c.x,n=g.y-c.y;if(e instanceof w){var h=0,l=g=0,u=0,m=1;if("gradient"==e.type_){h=e.x1_/f;c=e.y1_/d;var p=s(a,e.x0_/f,e.y0_/d),h=s(a,h,c),h=180*Math.atan2(h.x-p.x,h.y-p.y)/Math.PI;0>h&&(h+=360);1E-6>h&&(h=0)}else p=s(a,e.x0_,e.y0_),g=(p.x-c.x)/k,l=(p.y-c.y)/n,k/=f*q, +n/=d*q,m=x.max(k,n),u=2*e.r0_/m,m=2*e.r1_/m-u;f=e.colors_;f.sort(function(a,b){return a.offset-b.offset});d=f.length;p=f[0].color;c=f[d-1].color;k=f[0].alpha*a.globalAlpha;a=f[d-1].alpha*a.globalAlpha;for(var n=[],r=0;r')}else e instanceof +I?k&&n&&b.push("'):(e=G(a.fillStyle),b.push(''))}function s(a,b,c){a=a.m_;return{x:q*(b*a[0][0]+c*a[1][0]+a[2][0])-r,y:q*(b*a[0][1]+c*a[1][1]+a[2][1])-r}}function z(a,b,c){isFinite(b[0][0])&&(isFinite(b[0][1])&&isFinite(b[1][0])&&isFinite(b[1][1])&&isFinite(b[2][0])&&isFinite(b[2][1]))&&(a.m_=b,c&&(a.lineScale_=aa(ba(b[0][0]*b[1][1]-b[0][1]* +b[1][0]))))}function w(a){this.type_=a;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}function I(a,b){if(!a||1!=a.nodeType||"IMG"!=a.tagName)throw new A("TYPE_MISMATCH_ERR");if("complete"!=a.readyState)throw new A("INVALID_STATE_ERR");switch(b){case "repeat":case null:case "":this.repetition_="repeat";break;case "repeat-x":case "repeat-y":case "no-repeat":this.repetition_=b;break;default:throw new A("SYNTAX_ERR");}this.src_=a.src;this.width_=a.width;this.height_=a.height} +function A(a){this.code=this[a];this.message=a+": DOM Exception "+this.code}var x=Math,k=x.round,J=x.sin,K=x.cos,ba=x.abs,aa=x.sqrt,q=10,r=q/2;navigator.userAgent.match(/MSIE ([\d.]+)?/);var M=Array.prototype.slice;O(document);var U={init:function(a){a=a||document;a.createElement("canvas");a.attachEvent("onreadystatechange",W(this.init_,this,a))},init_:function(a){a=a.getElementsByTagName("canvas");for(var b=0;bd;d++)for(var B=0;16>B;B++)v[16*d+B]=d.toString(16)+B.toString(16);var Z={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC", +bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000", +darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082", +ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA", +mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5", +peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"}, +H={},L={},$={butt:"flat",round:"round"},d=C.prototype;d.clearRect=function(){this.textMeasureEl_&&(this.textMeasureEl_.removeNode(!0),this.textMeasureEl_=null);this.element_.innerHTML=""};d.beginPath=function(){this.currentPath_=[]};d.moveTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.lineTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.bezierCurveTo= +function(a,b,c,g,e,f){e=s(this,e,f);a=s(this,a,b);c=s(this,c,g);R(this,a,c,e)};d.quadraticCurveTo=function(a,b,c,g){a=s(this,a,b);c=s(this,c,g);g={x:this.currentX_+2/3*(a.x-this.currentX_),y:this.currentY_+2/3*(a.y-this.currentY_)};R(this,g,{x:g.x+(c.x-this.currentX_)/3,y:g.y+(c.y-this.currentY_)/3},c)};d.arc=function(a,b,c,g,e,f){c*=q;var d=f?"at":"wa",k=a+K(g)*c-r,n=b+J(g)*c-r;g=a+K(e)*c-r;e=b+J(e)*c-r;k!=g||f||(k+=0.125);a=s(this,a,b);k=s(this,k,n);g=s(this,g,e);this.currentPath_.push({type:d, +x:a.x,y:a.y,radius:c,xStart:k.x,yStart:k.y,xEnd:g.x,yEnd:g.y})};d.rect=function(a,b,c,g){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath()};d.strokeRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath();this.stroke();this.currentPath_=e};d.fillRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+ +c,b+g);this.lineTo(a,b+g);this.closePath();this.fill();this.currentPath_=e};d.createLinearGradient=function(a,b,c,g){var e=new w("gradient");e.x0_=a;e.y0_=b;e.x1_=c;e.y1_=g;return e};d.createRadialGradient=function(a,b,c,g,e,f){var d=new w("gradientradial");d.x0_=a;d.y0_=b;d.r0_=c;d.x1_=g;d.y1_=e;d.r1_=f;return d};d.drawImage=function(a,b){var c,g,e,d,r,y,n,h;e=a.runtimeStyle.width;d=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var l=a.width,u=a.height;a.runtimeStyle.width= +e;a.runtimeStyle.height=d;if(3==arguments.length)c=arguments[1],g=arguments[2],r=y=0,n=e=l,h=d=u;else if(5==arguments.length)c=arguments[1],g=arguments[2],e=arguments[3],d=arguments[4],r=y=0,n=l,h=u;else if(9==arguments.length)r=arguments[1],y=arguments[2],n=arguments[3],h=arguments[4],c=arguments[5],g=arguments[6],e=arguments[7],d=arguments[8];else throw Error("Invalid number of arguments");var m=s(this,c,g),p=[];p.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",p.join(""))};d.stroke=function(a){var b=[];b.push("d.x)d.x=f.x;if(null==c.y||f.yd.y)d.y=f.y}}b.push(' ">');a?T(this,b,c,d):S(this,b);b.push("");this.element_.insertAdjacentHTML("beforeEnd",b.join(""))};d.fill=function(){this.stroke(!0)};d.closePath=function(){this.currentPath_.push({type:"close"})};d.save=function(){var a= +{};P(this,a);this.aStack_.push(a);this.mStack_.push(this.m_);this.m_=t(D(),this.m_)};d.restore=function(){this.aStack_.length&&(P(this.aStack_.pop(),this),this.m_=this.mStack_.pop())};d.translate=function(a,b){z(this,t([[1,0,0],[0,1,0],[a,b,1]],this.m_),!1)};d.rotate=function(a){var b=K(a);a=J(a);z(this,t([[b,a,0],[-a,b,0],[0,0,1]],this.m_),!1)};d.scale=function(a,b){this.arcScaleX_*=a;this.arcScaleY_*=b;z(this,t([[a,0,0],[0,b,0],[0,0,1]],this.m_),!0)};d.transform=function(a,b,c,d,e,f){z(this,t([[a, +b,0],[c,d,0],[e,f,1]],this.m_),!0)};d.setTransform=function(a,b,c,d,e,f){z(this,[[a,b,0],[c,d,0],[e,f,1]],!0)};d.drawText_=function(a,b,c,d,e){var f=this.m_;d=0;var r=1E3,t=0,n=[],h;h=this.font;if(L[h])h=L[h];else{var l=document.createElement("div").style;try{l.font=h}catch(u){}h=L[h]={style:l.fontStyle||"normal",variant:l.fontVariant||"normal",weight:l.fontWeight||"normal",size:l.fontSize||10,family:l.fontFamily||"sans-serif"}}var l=h,m=this.element_;h={};for(var p in l)h[p]=l[p];p=parseFloat(m.currentStyle.fontSize); +m=parseFloat(l.size);"number"==typeof l.size?h.size=l.size:-1!=l.size.indexOf("px")?h.size=m:-1!=l.size.indexOf("em")?h.size=p*m:-1!=l.size.indexOf("%")?h.size=p/100*m:-1!=l.size.indexOf("pt")?h.size=m/0.75:h.size=p;h.size*=0.981;p=h.style+" "+h.variant+" "+h.weight+" "+h.size+"px "+h.family;m=this.element_.currentStyle;l=this.textAlign.toLowerCase();switch(l){case "left":case "center":case "right":break;case "end":l="ltr"==m.direction?"right":"left";break;case "start":l="rtl"==m.direction?"right": +"left";break;default:l="left"}switch(this.textBaseline){case "hanging":case "top":t=h.size/1.75;break;case "middle":break;default:case null:case "alphabetic":case "ideographic":case "bottom":t=-h.size/2.25}switch(l){case "right":d=1E3;r=0.05;break;case "center":d=r=500}b=s(this,b+0,c+t);n.push('');e?S(this,n):T(this,n,{x:-d,y:0}, +{x:r,y:h.size});e=f[0][0].toFixed(3)+","+f[1][0].toFixed(3)+","+f[0][1].toFixed(3)+","+f[1][1].toFixed(3)+",0,0";b=k(b.x/q)+","+k(b.y/q);n.push('','','');this.element_.insertAdjacentHTML("beforeEnd",n.join(""))};d.fillText=function(a,b,c,d){this.drawText_(a,b,c,d,!1)};d.strokeText=function(a, +b,c,d){this.drawText_(a,b,c,d,!0)};d.measureText=function(a){this.textMeasureEl_||(this.element_.insertAdjacentHTML("beforeEnd",''),this.textMeasureEl_=this.element_.lastChild);var b=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(b.createTextNode(a));return{width:this.textMeasureEl_.offsetWidth}};d.clip=function(){}; +d.arcTo=function(){};d.createPattern=function(a,b){return new I(a,b)};w.prototype.addColorStop=function(a,b){b=G(b);this.colors_.push({offset:a,color:b.color,alpha:b.alpha})};d=A.prototype=Error();d.INDEX_SIZE_ERR=1;d.DOMSTRING_SIZE_ERR=2;d.HIERARCHY_REQUEST_ERR=3;d.WRONG_DOCUMENT_ERR=4;d.INVALID_CHARACTER_ERR=5;d.NO_DATA_ALLOWED_ERR=6;d.NO_MODIFICATION_ALLOWED_ERR=7;d.NOT_FOUND_ERR=8;d.NOT_SUPPORTED_ERR=9;d.INUSE_ATTRIBUTE_ERR=10;d.INVALID_STATE_ERR=11;d.SYNTAX_ERR=12;d.INVALID_MODIFICATION_ERR= +13;d.NAMESPACE_ERR=14;d.INVALID_ACCESS_ERR=15;d.VALIDATION_ERR=16;d.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=U;CanvasRenderingContext2D=C;CanvasGradient=w;CanvasPattern=I;DOMException=A}(); +/*eslint-enable*/ +/*jshint ignore:end*/ \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/canvasjs.react.js b/frontend-syscop/src/canvasjs-2.3.1/canvasjs.react.js new file mode 100644 index 0000000..69c7951 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/canvasjs.react.js @@ -0,0 +1,48 @@ +var React = require('react'); +var CanvasJS = require('./canvasjs.min'); +CanvasJS = CanvasJS.Chart ? CanvasJS : window.CanvasJS; + +class CanvasJSChart extends React.Component { + static _cjsContainerId = 0 + constructor(props) { + super(props); + this.options = props.options ? props.options : {}; + this.containerProps = props.containerProps ? props.containerProps : {width: "100%", position: "relative"}; + this.containerProps.height = props.containerProps && props.containerProps.height ? props.containerProps.height : this.options.height ? this.options.height + "px" : "400px"; + this.chartContainerId = "canvasjs-react-chart-container-" + CanvasJSChart._cjsContainerId++; + } + componentDidMount() { + //Create Chart and Render + this.chart = new CanvasJS.Chart(this.chartContainerId, this.options); + this.chart.render(); + + if(this.props.onRef) + this.props.onRef(this.chart); + } + shouldComponentUpdate(nextProps, nextState){ + //Check if Chart-options has changed and determine if component has to be updated + return !(nextProps.options === this.options); + } + componentDidUpdate() { + //Update Chart Options & Render + this.chart.options = this.props.options; + this.chart.render(); + } + componentWillUnmount() { + //Destroy chart and remove reference + this.chart.destroy(); + if(this.props.onRef) + this.props.onRef(undefined); + } + render() { + //return React.createElement('div', { id: this.chartContainerId, style: this.containerProps }); + return
+ } +} + +var CanvasJSReact = { + CanvasJSChart: CanvasJSChart, + CanvasJS: CanvasJS +}; + +export default CanvasJSReact; \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/animated-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/animated-chart.html new file mode 100644 index 0000000..ef650af --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/animated-chart.html @@ -0,0 +1,43 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-from-json-data.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-from-json-data.html new file mode 100644 index 0000000..57e08a5 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-from-json-data.html @@ -0,0 +1,50 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-axis-scale-breaks.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-axis-scale-breaks.html new file mode 100644 index 0000000..3cb8789 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-axis-scale-breaks.html @@ -0,0 +1,51 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-crosshair.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-crosshair.html new file mode 100644 index 0000000..34e4625 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-crosshair.html @@ -0,0 +1,72 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-custom-legend-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-custom-legend-chart.html new file mode 100644 index 0000000..4f6e15c --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-custom-legend-chart.html @@ -0,0 +1,233 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-image-overlay.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-image-overlay.html new file mode 100644 index 0000000..50b7d73 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-image-overlay.html @@ -0,0 +1,108 @@ + + + + + + + + + + +
+ + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-index-data-label.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-index-data-label.html new file mode 100644 index 0000000..cd5dd33 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-index-data-label.html @@ -0,0 +1,46 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-inverted-reversed-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-inverted-reversed-axis.html new file mode 100644 index 0000000..0b69ea9 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-inverted-reversed-axis.html @@ -0,0 +1,46 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-logarithmic-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-logarithmic-axis.html new file mode 100644 index 0000000..895c4e2 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-logarithmic-axis.html @@ -0,0 +1,128 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-secondary-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-secondary-axis.html new file mode 100644 index 0000000..4e292db --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-secondary-axis.html @@ -0,0 +1,87 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-zoom-pan.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-zoom-pan.html new file mode 100644 index 0000000..79a3d41 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/chart-with-zoom-pan.html @@ -0,0 +1,40 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/dynamic-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/dynamic-chart.html new file mode 100644 index 0000000..a42de04 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/dynamic-chart.html @@ -0,0 +1,57 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/interactive-draggable-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/interactive-draggable-chart.html new file mode 100644 index 0000000..1750592 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/interactive-draggable-chart.html @@ -0,0 +1,115 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/multi-series-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/multi-series-chart.html new file mode 100644 index 0000000..f156e2f --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/multi-series-chart.html @@ -0,0 +1,94 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/null-data-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/null-data-chart.html new file mode 100644 index 0000000..2b0a5ed --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/null-data-chart.html @@ -0,0 +1,62 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/performance-demo.html b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/performance-demo.html new file mode 100644 index 0000000..7be550a --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/01-overview/performance-demo.html @@ -0,0 +1,43 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dashed-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dashed-line-chart.html new file mode 100644 index 0000000..b5b8e01 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dashed-line-chart.html @@ -0,0 +1,102 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dynamic-spline-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dynamic-spline-chart.html new file mode 100644 index 0000000..9bfb56e --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/dynamic-spline-chart.html @@ -0,0 +1,56 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-axis-scale-breaks.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-axis-scale-breaks.html new file mode 100644 index 0000000..9a6a355 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-axis-scale-breaks.html @@ -0,0 +1,71 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-data-markers.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-data-markers.html new file mode 100644 index 0000000..8b620e2 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-data-markers.html @@ -0,0 +1,53 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-logarithmic-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-logarithmic-axis.html new file mode 100644 index 0000000..4a3430a --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-logarithmic-axis.html @@ -0,0 +1,107 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-multiple-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-multiple-axis.html new file mode 100644 index 0000000..e4626cc --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-multiple-axis.html @@ -0,0 +1,125 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-zoom-pan.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-zoom-pan.html new file mode 100644 index 0000000..24a15c6 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart-with-zoom-pan.html @@ -0,0 +1,46 @@ + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart.html new file mode 100644 index 0000000..d21bd73 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/line-chart.html @@ -0,0 +1,44 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-line-chart.html new file mode 100644 index 0000000..c25f9ae --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-line-chart.html @@ -0,0 +1,257 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-spline-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-spline-chart.html new file mode 100644 index 0000000..7c4d43d --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-spline-chart.html @@ -0,0 +1,171 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-step-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-step-line-chart.html new file mode 100644 index 0000000..d6d67a1 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/multi-series-step-line-chart.html @@ -0,0 +1,67 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-legends.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-legends.html new file mode 100644 index 0000000..34c7c5b --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-legends.html @@ -0,0 +1,123 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-secondary-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-secondary-axis.html new file mode 100644 index 0000000..3cdb825 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart-with-secondary-axis.html @@ -0,0 +1,100 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart.html new file mode 100644 index 0000000..7c1afce --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/spline-chart.html @@ -0,0 +1,55 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/step-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/step-line-chart.html new file mode 100644 index 0000000..dc1a4b7 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/02-line-spline-step-line-charts/step-line-chart.html @@ -0,0 +1,57 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/area-chart.html new file mode 100644 index 0000000..f721ee4 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/area-chart.html @@ -0,0 +1,50 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-area-chart.html new file mode 100644 index 0000000..5e0ce68 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-area-chart.html @@ -0,0 +1,73 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-range-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-range-area-chart.html new file mode 100644 index 0000000..1bd7540 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-range-area-chart.html @@ -0,0 +1,127 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-spline-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-spline-area-chart.html new file mode 100644 index 0000000..54e2bec --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/multi-series-spline-area-chart.html @@ -0,0 +1,113 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-area-chart.html new file mode 100644 index 0000000..e5408a6 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-area-chart.html @@ -0,0 +1,72 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-spline-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-spline-area-chart.html new file mode 100644 index 0000000..280a052 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/range-spline-area-chart.html @@ -0,0 +1,51 @@ + + + + + + + +
+ + + diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/spline-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/spline-area-chart.html new file mode 100644 index 0000000..450b4a5 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/spline-area-chart.html @@ -0,0 +1,55 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart-with-date-time-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart-with-date-time-axis.html new file mode 100644 index 0000000..76483f3 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart-with-date-time-axis.html @@ -0,0 +1,120 @@ + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart.html new file mode 100644 index 0000000..7af1927 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-100-chart.html @@ -0,0 +1,78 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-chart.html new file mode 100644 index 0000000..90e29ed --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/stacked-area-chart.html @@ -0,0 +1,68 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/step-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/step-area-chart.html new file mode 100644 index 0000000..7fb51f0 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/03-stacked-spline-range-area-charts/step-area-chart.html @@ -0,0 +1,42 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart-with-axis-scale-break.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart-with-axis-scale-break.html new file mode 100644 index 0000000..5e7f2a0 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart-with-axis-scale-break.html @@ -0,0 +1,58 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart.html new file mode 100644 index 0000000..d12b9cf --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/bar-chart.html @@ -0,0 +1,58 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart-with-multiple-axis.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart-with-multiple-axis.html new file mode 100644 index 0000000..982c84d --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart-with-multiple-axis.html @@ -0,0 +1,83 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart.html new file mode 100644 index 0000000..db12606 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/column-chart.html @@ -0,0 +1,43 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-bar-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-bar-chart.html new file mode 100644 index 0000000..69e6bb3 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-bar-chart.html @@ -0,0 +1,104 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-range-column-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-range-column-chart.html new file mode 100644 index 0000000..a90e357 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-range-column-chart.html @@ -0,0 +1,89 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-waterfall-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-waterfall-chart.html new file mode 100644 index 0000000..3e473fb --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/multi-series-waterfall-chart.html @@ -0,0 +1,98 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-bar-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-bar-chart.html new file mode 100644 index 0000000..4a8b95f --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-bar-chart.html @@ -0,0 +1,49 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-column-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-column-chart.html new file mode 100644 index 0000000..2482015 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/range-column-chart.html @@ -0,0 +1,49 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-100-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-100-chart.html new file mode 100644 index 0000000..a0fe8f0 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-100-chart.html @@ -0,0 +1,76 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-chart.html new file mode 100644 index 0000000..e53506c --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-bar-chart.html @@ -0,0 +1,126 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-100-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-100-chart.html new file mode 100644 index 0000000..39e9e1b --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-100-chart.html @@ -0,0 +1,103 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-chart.html new file mode 100644 index 0000000..f377f68 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/stacked-column-chart.html @@ -0,0 +1,113 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart-with-custom-color.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart-with-custom-color.html new file mode 100644 index 0000000..bed3a0a --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart-with-custom-color.html @@ -0,0 +1,51 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart.html new file mode 100644 index 0000000..cf00ff2 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/04-column-stacked-range-waterfall-charts/waterfall-chart.html @@ -0,0 +1,48 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-Chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-Chart.html new file mode 100644 index 0000000..7ad4552 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-Chart.html @@ -0,0 +1,40 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-chart-with-custom-inner-radius.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-chart-with-custom-inner-radius.html new file mode 100644 index 0000000..7e9b429 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/doughnut-chart-with-custom-inner-radius.html @@ -0,0 +1,55 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart-with-custom-neck.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart-with-custom-neck.html new file mode 100644 index 0000000..b5d9fb7 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart-with-custom-neck.html @@ -0,0 +1,52 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart.html new file mode 100644 index 0000000..8aa524e --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/funnel-chart.html @@ -0,0 +1,52 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/inverted-reversed-funnel-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/inverted-reversed-funnel-chart.html new file mode 100644 index 0000000..3b475ef --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/inverted-reversed-funnel-chart.html @@ -0,0 +1,54 @@ + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-custom-radius.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-custom-radius.html new file mode 100644 index 0000000..770bc9d --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-custom-radius.html @@ -0,0 +1,51 @@ + + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-legends.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-legends.html new file mode 100644 index 0000000..e1bd01b --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart-with-legends.html @@ -0,0 +1,52 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart.html new file mode 100644 index 0000000..7dd8a3e --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pie-chart.html @@ -0,0 +1,36 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-where-area-represents-value.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-where-area-represents-value.html new file mode 100644 index 0000000..c84b2a9 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-where-area-represents-value.html @@ -0,0 +1,39 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-with-index-label-placed-Inside.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-with-index-label-placed-Inside.html new file mode 100644 index 0000000..e39048c --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart-with-index-label-placed-Inside.html @@ -0,0 +1,40 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart.html new file mode 100644 index 0000000..b9053b2 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/05-pie-doughnut-funnel-pyramid-charts/pyramid-chart.html @@ -0,0 +1,40 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/candlestick-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/candlestick-line-chart.html new file mode 100644 index 0000000..72db3b3 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/candlestick-line-chart.html @@ -0,0 +1,108 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/multi-series-candlestick-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/multi-series-candlestick-chart.html new file mode 100644 index 0000000..35871f6 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/multi-series-candlestick-chart.html @@ -0,0 +1,90 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-chart-from-json-data.html b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-chart-from-json-data.html new file mode 100644 index 0000000..3cca87c --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-chart-from-json-data.html @@ -0,0 +1,58 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-stock-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-stock-chart.html new file mode 100644 index 0000000..ee55566 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/06-candlestick-ohlc-charts/ohlc-stock-chart.html @@ -0,0 +1,54 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-data-marker.html b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-data-marker.html new file mode 100644 index 0000000..580047d --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-data-marker.html @@ -0,0 +1,59 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-zoom-pan.html b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-zoom-pan.html new file mode 100644 index 0000000..2670b87 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart-with-zoom-pan.html @@ -0,0 +1,60 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart.html new file mode 100644 index 0000000..a0980da --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/bubble-chart.html @@ -0,0 +1,69 @@ + + + + + + + +
+ + + diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/multi-series-scatter-point-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/multi-series-scatter-point-chart.html new file mode 100644 index 0000000..7a3212e --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/multi-series-scatter-point-chart.html @@ -0,0 +1,73 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart-with-custom-marker.html b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart-with-custom-marker.html new file mode 100644 index 0000000..eb0a7ab --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart-with-custom-marker.html @@ -0,0 +1,129 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart.html new file mode 100644 index 0000000..39a2857 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/07-scatter-bubble-charts/scatter-point-chart.html @@ -0,0 +1,63 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-custom-color.html b/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-custom-color.html new file mode 100644 index 0000000..c38a46a --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-custom-color.html @@ -0,0 +1,43 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-outliers.html b/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-outliers.html new file mode 100644 index 0000000..c7a1688 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart-with-outliers.html @@ -0,0 +1,81 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart.html new file mode 100644 index 0000000..327b5f6 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/08-box-and-whisker-charts/box-and-whisker-chart.html @@ -0,0 +1,43 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/column-line-area-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/column-line-area-chart.html new file mode 100644 index 0000000..1d9c873 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/column-line-area-chart.html @@ -0,0 +1,122 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-bar-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-bar-chart.html new file mode 100644 index 0000000..01b9a6a --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-bar-chart.html @@ -0,0 +1,54 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-chart.html new file mode 100644 index 0000000..ddb9026 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-chart.html @@ -0,0 +1,58 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-line-chart.html new file mode 100644 index 0000000..b6f1cc7 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/error-line-chart.html @@ -0,0 +1,73 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/ohlc-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/ohlc-line-chart.html new file mode 100644 index 0000000..24674ce --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/ohlc-line-chart.html @@ -0,0 +1,106 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart-with-index-data-label.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart-with-index-data-label.html new file mode 100644 index 0000000..fd35f55 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart-with-index-data-label.html @@ -0,0 +1,68 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart.html new file mode 100644 index 0000000..393d6af --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/pareto-chart.html @@ -0,0 +1,66 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/range-area-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/range-area-line-chart.html new file mode 100644 index 0000000..be32272 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/09-combination-charts/range-area-line-chart.html @@ -0,0 +1,108 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-column-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-column-chart.html new file mode 100644 index 0000000..392bb3c --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-column-chart.html @@ -0,0 +1,54 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-line-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-line-chart.html new file mode 100644 index 0000000..988c221 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-line-chart.html @@ -0,0 +1,58 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-multi-series-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-multi-series-chart.html new file mode 100644 index 0000000..2dbd00d --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/10-dynamic-charts/dynamic-multi-series-chart.html @@ -0,0 +1,113 @@ + + + + + + + +
+ + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-charts.html b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-charts.html new file mode 100644 index 0000000..d6d4207 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-charts.html @@ -0,0 +1,42 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-line-chart-with-zoom-pan.html b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-line-chart-with-zoom-pan.html new file mode 100644 index 0000000..c139f40 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-line-chart-with-zoom-pan.html @@ -0,0 +1,49 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-resizable-chart.html b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-resizable-chart.html new file mode 100644 index 0000000..31523b1 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-resizable-chart.html @@ -0,0 +1,55 @@ + + + + + + + + +
+
+
+ + + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-area-chart-in-tab.html b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-area-chart-in-tab.html new file mode 100644 index 0000000..b35ebf4 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-area-chart-in-tab.html @@ -0,0 +1,98 @@ + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-chart-with-image-export.html b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-chart-with-image-export.html new file mode 100644 index 0000000..a3e82d0 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/examples/11-integration/jquery-spline-chart-with-image-export.html @@ -0,0 +1,41 @@ + + + + + + + +
+ + + + \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/instruction.txt b/frontend-syscop/src/canvasjs-2.3.1/instruction.txt new file mode 100644 index 0000000..93eb448 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/instruction.txt @@ -0,0 +1,4 @@ +For standalone version include canvasjs.min.js +For jQuery version include jquery.canvasjs.min.js + +** DO NOT include both the files ** \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/jquery.canvasjs.min.js b/frontend-syscop/src/canvasjs-2.3.1/jquery.canvasjs.min.js new file mode 100644 index 0000000..8785b49 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/jquery.canvasjs.min.js @@ -0,0 +1,922 @@ +/* + CanvasJS HTML5 & JavaScript Charts - v2.3.1 GA - https://canvasjs.com/ + Copyright 2018 fenopix + + --------------------- License Information -------------------- + CanvasJS is a commercial product which requires purchase of license. Without a commercial license you can use it for evaluation purposes for upto 30 days. Please refer to the following link for further details. + https://canvasjs.com/license/ + +*/ +/*eslint-disable*/ +/*jshint ignore:start*/ +(function(){function qa(k,p){k.prototype=eb(p.prototype);k.prototype.constructor=k;k.base=p.prototype}function eb(k){function p(){}p.prototype=k;return new p}function Ya(k,p,D){"millisecond"===D?k.setMilliseconds(k.getMilliseconds()+1*p):"second"===D?k.setSeconds(k.getSeconds()+1*p):"minute"===D?k.setMinutes(k.getMinutes()+1*p):"hour"===D?k.setHours(k.getHours()+1*p):"day"===D?k.setDate(k.getDate()+1*p):"week"===D?k.setDate(k.getDate()+7*p):"month"===D?k.setMonth(k.getMonth()+1*p):"year"===D&&k.setFullYear(k.getFullYear()+ +1*p);return k}function $(k,p){var D=!1;0>k&&(D=!0,k*=-1);k=""+k;for(p=p?p:1;k.length
Please right click on the image and save it to your device
"), +p.document.close()}}}function N(k){var p=((k&16711680)>>16).toString(16),D=((k&65280)>>8).toString(16);k=((k&255)>>0).toString(16);p=2>p.length?"0"+p:p;D=2>D.length?"0"+D:D;k=2>k.length?"0"+k:k;return"#"+p+D+k}function fb(k,p){var D=this.length>>>0,r=Number(p)||0,r=0>r?Math.ceil(r):Math.floor(r);for(0>r&&(r+=D);rD;D++)if(k[D]!==k[D+4]|k[D]!==k[D+8]|k[D]!==k[D+12]){p=!1;break}return p?k[0]<<16|k[1]<<8|k[2]:0}function na(k,p,D){return k in p?p[k]:D[k]}function Oa(k,p,D){if(r&&bb){var u=k.getContext("2d");Pa=u.webkitBackingStorePixelRatio|| +u.mozBackingStorePixelRatio||u.msBackingStorePixelRatio||u.oBackingStorePixelRatio||u.backingStorePixelRatio||1;W=Ua/Pa;k.width=p*W;k.height=D*W;Ua!==Pa&&(k.style.width=p+"px",k.style.height=D+"px",u.scale(W,W))}else k.width=p,k.height=D}function hb(k){if(!ib){var p=!1,D=!1;"undefined"===typeof ra.Chart.creditHref?(k.creditHref=ja("iuuqr;..b`ow`rkr/bnl."),k.creditText=ja("B`ow`rKR/bnl")):(p=k.updateOption("creditText"),D=k.updateOption("creditHref"));if(k.creditHref&&k.creditText){k._creditLink|| +(k._creditLink=document.createElement("a"),k._creditLink.setAttribute("class","canvasjs-chart-credit"),k._creditLink.setAttribute("title","JavaScript Charts"),k._creditLink.setAttribute("style","outline:none;margin:0px;position:absolute;right:2px;top:"+(k.height-14)+"px;color:dimgrey;text-decoration:none;font-size:11px;font-family: Calibri, Lucida Grande, Lucida Sans Unicode, Arial, sans-serif"),k._creditLink.setAttribute("tabIndex",-1),k._creditLink.setAttribute("target","_blank"));if(0===k.renderCount|| +p||D)k._creditLink.setAttribute("href",k.creditHref),k._creditLink.innerHTML=k.creditText;k._creditLink&&k.creditHref&&k.creditText?(k._creditLink.parentElement||k._canvasJSContainer.appendChild(k._creditLink),k._creditLink.style.top=k.height-14+"px"):k._creditLink.parentElement&&k._canvasJSContainer.removeChild(k._creditLink)}}}function ta(k,p){Ja&&(this.canvasCount|=0,window.console.log(++this.canvasCount));var D=document.createElement("canvas");D.setAttribute("class","canvasjs-chart-canvas");Oa(D, +k,p);r||"undefined"===typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(D);return D}function sa(k,p,D){for(var r in D)p.style[r]=D[r]}function ua(k,p,D){p.getAttribute("state")||(p.style.backgroundColor=k.toolbar.backgroundColor,p.style.color=k.toolbar.fontColor,p.style.border="none",sa(k,p,{WebkitUserSelect:"none",MozUserSelect:"none",msUserSelect:"none",userSelect:"none"}));p.getAttribute("state")!==D&&(p.setAttribute("state",D),p.setAttribute("type","button"),sa(k,p,{padding:"5px 12px", +cursor:"pointer","float":"left",width:"40px",height:"25px",outline:"0px",verticalAlign:"baseline",lineHeight:"0"}),p.setAttribute("title",k._cultureInfo[D+"Text"]),p.innerHTML=""+k._cultureInfo[D+"Text"]+"")}function Qa(){for(var k=null,p=0;pa?"a":"p";case "tt":return 12>a?"am":"pm";case "T":return 12>a?"A": +"P";case "TT":return 12>a?"AM":"PM";case "K":return S?"UTC":(String(z).match(H)||[""]).pop().replace(F,"");case "z":return(0k?!0:!1;u&&(k*=-1);var v=r?r.decimalSeparator:".",H=r?r.digitGroupSeparator: +",",F="";p=String(p);var F=1,z=r="",E=-1,L=[],R=[],I=0,N=0,S=0,O=!1,U=0,z=p.match(/"[^"]*"|'[^']*'|[eE][+-]*[0]+|[,]+[.]|\u2030|./g);p=null;for(var Q=0;z&&QE)E=Q;else{if("%"===p)F*=100;else if("\u2030"===p){F*=1E3;continue}else if(","===p[0]&&"."===p[p.length-1]){F/=Math.pow(1E3,p.length-1);E=Q+p.length-1;continue}else"E"!==p[0]&&"e"!==p[0]||"0"!==p[p.length-1]||(O=!0);0>E?(L.push(p),"#"===p||"0"===p?I++:","===p&&S++):(R.push(p),"#"!==p&&"0"!==p||N++)}O&&(p=Math.floor(k), +z=-Math.floor(Math.log(k)/Math.LN10+1),U=0===k?0:0===p?-(I+z):String(p).length-I,F/=Math.pow(10,U));0>E&&(E=Q);F=(k*F).toFixed(N);p=F.split(".");F=(p[0]+"").split("");k=(p[1]+"").split("");F&&"0"===F[0]&&F.shift();for(O=z=Q=N=E=0;0U?p.replace("+","").replace("-",""):p.replace("-",""),r+=p.replace(/[0]+/,function(k){return $(U,k.length)}));H="";for(L=!1;0U?p.replace("+","").replace("-",""):p.replace("-",""),H+=p.replace(/[0]+/,function(k){return $(U,k.length)}));r+=(L?v:"")+H;return u?"-"+r:r},Ra=function(k){var p=0,r=0;k=k||window.event;k.offsetX||0===k.offsetX?(p=k.offsetX,r=k.offsetY):k.layerX||0==k.layerX?(p=k.layerX,r=k.layerY):(p=k.pageX-k.target.offsetLeft, +r=k.pageY-k.target.offsetTop);return{x:p,y:r}},bb=!0,Ua=window.devicePixelRatio||1,Pa=1,W=bb?Ua/Pa:1,ea=function(k,p,r,u,v,H,F,z,E,L,R,N,O){"undefined"===typeof O&&(O=1);F=F||0;z=z||"black";var I=15p)v=H-1;else break}r>p&&1H&&(F=p.pop(),u-=F.height,v=z)}this._wrappedText={lines:p,width:v,height:u};this.width=v+(this.leftPadding+this.rightPadding);this.height=u+(this.topPadding+this.bottomPadding);this.ctx.font=r};ka.prototype._getFontString=function(){var k;k=""+(this.fontStyle?this.fontStyle+" ":"");k+=this.fontWeight?this.fontWeight+" ":"";k+=this.fontSize?this.fontSize+"px ":"";var p=this.fontFamily?this.fontFamily+"":"";!r&&p&&(p=p.split(",")[0],"'"!==p[0]&&'"'!==p[0]&&(p="'"+p+"'"));return k+=p}; +qa(Va,V);qa(Aa,V);Aa.prototype.setLayout=function(){if(this.text){var k=this.dockInsidePlotArea?this.chart.plotArea:this.chart,p=k.layoutManager.getFreeSpace(),r=p.x1,v=p.y1,E=0,H=0,F=this.chart._menuButton&&this.chart.exportEnabled&&"top"===this.verticalAlign?22:0,z,I;"top"===this.verticalAlign||"bottom"===this.verticalAlign?(null===this.maxWidth&&(this.maxWidth=p.width-4-F*("center"===this.horizontalAlign?2:1)),H=0.5*p.height-this.margin-2,E=0):"center"===this.verticalAlign&&("left"===this.horizontalAlign|| +"right"===this.horizontalAlign?(null===this.maxWidth&&(this.maxWidth=p.height-4),H=0.5*p.width-this.margin-2):"center"===this.horizontalAlign&&(null===this.maxWidth&&(this.maxWidth=p.width-4),H=0.5*p.height-4));var L;u(this.padding)||"number"!==typeof this.padding?u(this.padding)||"object"!==typeof this.padding||(L=this.padding.top?this.padding.top:this.padding.bottom?this.padding.bottom:0,L+=this.padding.bottom?this.padding.bottom:this.padding.top?this.padding.top:0,L*=1.25):L=2.5*this.padding;this.wrap|| +(H=Math.min(H,Math.max(1.5*this.fontSize,this.fontSize+L)));H=new ka(this.ctx,{fontSize:this.fontSize,fontFamily:this.fontFamily,fontColor:this.fontColor,fontStyle:this.fontStyle,fontWeight:this.fontWeight,horizontalAlign:this.horizontalAlign,verticalAlign:this.verticalAlign,borderColor:this.borderColor,borderThickness:this.borderThickness,backgroundColor:this.backgroundColor,maxWidth:this.maxWidth,maxHeight:H,cornerRadius:this.cornerRadius,text:this.text,padding:this.padding,textBaseline:"top"}); +L=H.measureText();"top"===this.verticalAlign||"bottom"===this.verticalAlign?("top"===this.verticalAlign?(v=p.y1+2,I="top"):"bottom"===this.verticalAlign&&(v=p.y2-2-L.height,I="bottom"),"left"===this.horizontalAlign?r=p.x1+2:"center"===this.horizontalAlign?r=p.x1+p.width/2-L.width/2:"right"===this.horizontalAlign&&(r=p.x2-2-L.width-F),z=this.horizontalAlign,this.width=L.width,this.height=L.height):"center"===this.verticalAlign&&("left"===this.horizontalAlign?(r=p.x1+2,v=p.y2-2-(this.maxWidth/2-L.width/ +2),E=-90,I="left",this.width=L.height,this.height=L.width):"right"===this.horizontalAlign?(r=p.x2-2,v=p.y1+2+(this.maxWidth/2-L.width/2),E=90,I="right",this.width=L.height,this.height=L.width):"center"===this.horizontalAlign&&(v=k.y1+(k.height/2-L.height/2),r=k.x1+(k.width/2-L.width/2),I="center",this.width=L.width,this.height=L.height),z="center");H.x=r;H.y=v;H.angle=E;H.horizontalAlign=z;this._textBlock=H;k.layoutManager.registerSpace(I,{width:this.width+("left"===I||"right"===I?this.margin+2:0), +height:this.height+("top"===I||"bottom"===I?this.margin+2:0)});this.bounds={x1:r,y1:v,x2:r+this.width,y2:v+this.height};this.ctx.textBaseline="top"}};Aa.prototype.render=function(){this._textBlock&&this._textBlock.render(!0)};qa(Ka,V);Ka.prototype.setLayout=Aa.prototype.setLayout;Ka.prototype.render=Aa.prototype.render;Wa.prototype.get=function(k,p){var r=null;0a[g].x&&0w?{x:a[l].x+w/3,y:a[l].y+c/3}:{x:a[l].x,y:a[l].y+c/9};l=e;g=0===l?0:l-1;m=l===a.length-1?l:l+1;c=Math.abs((a[m].x-a[g].x)/(0===a[l].x-a[g].x?0.01:a[l].x-a[g].x))*(d- +1)/2+1;w=(a[m].x-a[g].x)/c;c=(a[m].y-a[g].y)/c;b[b.length]=a[l].x>a[g].x&&0w?{x:a[l].x-w/3,y:a[l].y-c/3}:{x:a[l].x,y:a[l].y-c/9};b[b.length]=a[e]}return b}function E(a,d,b,c,e,g,m,l,w,h){var s=0;h?(m.color=g,l.color=g):h=1;s=w?Math.abs(e-b):Math.abs(c-d);s=0this.labelAngle?this.labelAngle-=180:270<=this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360);this.options.scaleBreaks&&(this.scaleBreaks=new Q(this.chart, +this.options.scaleBreaks,++this.chart._eventManager.lastObjectId,this));this.stripLines=[];if(this.options.stripLines&&0=this._appliedBreaks[a+1].startValue&&(this._appliedBreaks[a].endValue=Math.max(this._appliedBreaks[a].endValue,this._appliedBreaks[a+1].endValue),window.console&&window.console.log("CanvasJS Error: Breaks "+a+" and "+(a+1)+" are overlapping."),this._appliedBreaks.splice(a,2),a--)}}function L(a,d,b,c,e,g){L.base.constructor.call(this,"Break",d,b,c,g);this.id=e;this.chart=a;this.ctx=this.chart.ctx;this.scaleBreaks=g;this.optionsName= +d;this.isOptionsInArray=!0;this.type=b.type?this.type:g.type;this.fillOpacity=u(b.fillOpacity)?g.fillOpacity:this.fillOpacity;this.lineThickness=u(b.lineThickness)?g.lineThickness:this.lineThickness;this.color=b.color?this.color:g.color;this.lineColor=b.lineColor?this.lineColor:g.lineColor;this.lineDashType=b.lineDashType?this.lineDashType:g.lineDashType;!u(this.startValue)&&this.startValue.getTime&&(this.startValue=this.startValue.getTime());!u(this.endValue)&&this.endValue.getTime&&(this.endValue= +this.endValue.getTime());"number"===typeof this.startValue&&("number"===typeof this.endValue&&this.endValue=navigator.userAgent.search("MSIE")&&sa(a,a._zoomButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers);O(this._zoomButton,"mouseout",function(){d||(sa(a,a._zoomButton,{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._zoomButton.childNodes[0],{WebkitFilter:"invert(0%)", +filter:"invert(0%)"}))},this.allDOMEventHandlers)}this._resetButton||(d=!1,va(this._resetButton=document.createElement("button")),ua(this,this._resetButton,"reset"),this._resetButton.style.borderRight=(this.exportEnabled?this.toolbar.borderThickness:0)+"px solid "+this.toolbar.borderColor,this._toolBar.appendChild(this._resetButton),O(this._resetButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers),O(this._resetButton,"click",function(){a.toolTip.hide();a.zoomEnabled||a.panEnabled?(a.zoomEnabled= +!0,a.panEnabled=!1,ua(a,a._zoomButton,"pan"),a._defaultCursor="default",a.overlaidCanvas.style.cursor=a._defaultCursor):(a.zoomEnabled=!1,a.panEnabled=!1);if(a.sessionVariables.axisX)for(var c=0;c=navigator.userAgent.search("MSIE")&&sa(a,a._resetButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers),O(this._resetButton,"mouseout",function(){d||(sa(a,a._resetButton, +{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._resetButton.childNodes[0],{WebkitFilter:"invert(0%)",filter:"invert(0%)"}))},this.allDOMEventHandlers),this.overlaidCanvas.style.cursor=a._defaultCursor);this.zoomEnabled||this.panEnabled||(this._zoomButton?(a._zoomButton.getAttribute("state")===a._cultureInfo.zoomText?(this.panEnabled=!0,this.zoomEnabled=!1):(this.zoomEnabled=!0,this.panEnabled= +!1),Qa(a._zoomButton,a._resetButton)):(this.zoomEnabled=!0,this.panEnabled=!1))}else this.panEnabled=this.zoomEnabled=!1;this._menuButton?this.exportEnabled?Qa(this._menuButton):va(this._menuButton):this.exportEnabled&&r&&(d=!1,this._menuButton=document.createElement("button"),ua(this,this._menuButton,"menu"),this._toolBar.appendChild(this._menuButton),O(this._menuButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers),O(this._menuButton,"click",function(){"none"!==a._dropdownMenu.style.display|| +a._dropDownCloseTime&&500>=(new Date).getTime()-a._dropDownCloseTime.getTime()||(a._dropdownMenu.style.display="block",a._menuButton.blur(),a._dropdownMenu.focus())},this.allDOMEventHandlers,!0),O(this._menuButton,"mouseover",function(){d||(sa(a,a._menuButton,{backgroundColor:a.toolbar.backgroundColorOnHover,color:a.toolbar.fontColorOnHover}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._menuButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers,!0), +O(this._menuButton,"mouseout",function(){d||(sa(a,a._menuButton,{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._menuButton.childNodes[0],{WebkitFilter:"invert(0%)",filter:"invert(0%)"}))},this.allDOMEventHandlers,!0));if(!this._dropdownMenu&&this.exportEnabled&&r){d=!1;this._dropdownMenu=document.createElement("div");this._dropdownMenu.setAttribute("tabindex",-1);var b=-1!==this.theme.indexOf("dark")?"black":"#888888";this._dropdownMenu.style.cssText= +"position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 0px;top: 25px;min-width: 120px;outline: 0;font-size: 14px; font-family: Arial, Helvetica, sans-serif;padding: 5px 0px 5px 0px;text-align: left;line-height: 10px;background-color:"+this.toolbar.backgroundColor+";box-shadow: 2px 2px 10px "+b;a._dropdownMenu.style.display="none";this._toolBar.appendChild(this._dropdownMenu);O(this._dropdownMenu,"blur",function(){va(a._dropdownMenu); +a._dropDownCloseTime=new Date},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText="padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.printText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers, +!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){a.print();va(a._dropdownMenu)},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText="padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.saveJPGText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d= +!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){Ta(a.canvas,"jpeg",a.exportFileName);va(a._dropdownMenu)},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText= +"padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.savePNGText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor, +this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){Ta(a.canvas,"png",a.exportFileName);va(a._dropdownMenu)},this.allDOMEventHandlers,!0)}"none"!==this._toolBar.style.display&&this._zoomButton&&(this.panEnabled?ua(a,a._zoomButton,"zoom"):ua(a,a._zoomButton,"pan"),a._resetButton.getAttribute("state")!==a._cultureInfo.resetText&&ua(a,a._resetButton,"reset"));this.options.toolTip&&this.toolTip.options!==this.options.toolTip&&(this.toolTip.options=this.options.toolTip); +for(var c in this.toolTip.options)this.toolTip.options.hasOwnProperty(c)&&this.toolTip.updateOption(c)};p.prototype._updateSize=function(){var a;a=[this.canvas,this._preRenderCanvas,this.overlaidCanvas,this._eventManager.ghostCanvas];var d=0,b=0;this.options.width?d=this.width:this.width=d=0c.linkedDataSeriesIndex||c.linkedDataSeriesIndex>=this.options.data.length||"number"!==typeof c.linkedDataSeriesIndex|| +"error"===this.options.data[c.linkedDataSeriesIndex].type)&&(c.linkedDataSeriesIndex=null);null===c.name&&(c.name="DataSeries "+a);null===c.color?1a&&"undefined"!==typeof w.startTimePercent?a>=w.startTimePercent&&w.animationCallback(w.easingFunction(a-w.startTimePercent,0,1,1-w.startTimePercent),w):w.animationCallback(w.easingFunction(a,0,1,1),w);s.dispatchEvent("dataAnimationIterationEnd",{chart:s})},function(){b=[];for(var a=0;aa.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=0;mb.max&&(b.max=c);ed.max&&"number"===typeof e&&(d.max=e);if(0B&&(B=1/B);b.minDiff>B&&1!==B&&(b.minDiff=B)}else B=c-l.dataPoints[w-1].x,0>B&&(B*=-1),b.minDiff>B&&0!==B&&(b.minDiff=B);null!==e&&null!==l.dataPoints[w-1].y&&(a.axisY.logarithmic?(B=e/l.dataPoints[w-1].y,1>B&&(B=1/B),d.minDiff>B&&1!==B&&(d.minDiff=B)):(B=e-l.dataPoints[w-1].y,0>B&&(B*=-1),d.minDiff>B&&0!==B&&(d.minDiff=B)))}if(cf&& +!s)s=!0;else if(c>f&&s)continue;l.dataPoints[w].label&&(a.axisX.labels[c]=l.dataPoints[w].label);cb.viewPortMax&&(b.viewPortMax=c);null===e?b.viewPortMin===c&&qd.viewPortMax&&"number"===typeof e&&(d.viewPortMax=e))}}l.axisX.valueType=l.xValueType=g?"dateTime":"number"}};p.prototype._processStackedPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var d= +a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=[],l=[],w=Infinity,h=-Infinity,s=0;sb.max&&(b.max=c);if(0r&&(r=1/r);b.minDiff>r&&1!==r&&(b.minDiff=r)}else r=c-q.dataPoints[n-1].x,0>r&&(r*=-1),b.minDiff>r&&0!==r&&(b.minDiff=r);null!==e&&null!==q.dataPoints[n-1].y&&(a.axisY.logarithmic?0r&&(r=1/r),d.minDiff>r&&1!==r&&(d.minDiff=r)):(r=e-q.dataPoints[n-1].y,0>r&&(r*=-1),d.minDiff>r&&0!==r&&(d.minDiff=r)))}if(ct&&!B)B=!0;else if(c>t&&B)continue;q.dataPoints[n].label&&(a.axisX.labels[c]=q.dataPoints[n].label);cb.viewPortMax&&(b.viewPortMax=c);null===q.dataPoints[n].y?b.viewPortMin===c&&kd.max&&(d.max=a),nb.viewPortMax||(ad.viewPortMax&& +(d.viewPortMax=a)));for(n in l)l.hasOwnProperty(n)&&!isNaN(n)&&(a=l[n],ad.max&&(d.max=Math.max(a,h)),nb.viewPortMax||(ad.viewPortMax&&(d.viewPortMax=Math.max(a,h))))}};p.prototype._processStacked100PlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=!1,l=!1,w=[],h=0;hb.max&&(b.max=c);if(0t&&(t=1/t);b.minDiff>t&&1!==t&&(b.minDiff=t)}else t=c-s.dataPoints[q-1].x,0>t&&(t*=-1),b.minDiff>t&&0!==t&&(b.minDiff=t);u(e)||null===s.dataPoints[q-1].y||(a.axisY.logarithmic?0t&&(t=1/t),d.minDiff>t&&1!==t&&(d.minDiff=t)):(t=e-s.dataPoints[q-1].y,0>t&&(t*=-1),d.minDiff>t&&0!==t&&(d.minDiff=t)))}if(cr&&!f)f=!0;else if(c>r&&f)continue;s.dataPoints[q].label&&(a.axisX.labels[c]=s.dataPoints[q].label); +cb.viewPortMax&&(b.viewPortMax=c);null===e?b.viewPortMin===c&&Be&&(l=!0),w[c]=w[c]?w[c]+Math.abs(e):Math.abs(e))}}s.axisX.valueType=s.xValueType=g?"dateTime":"number"}a.axisY.logarithmic?(d.max=u(d.viewPortMax)?99*Math.pow(a.axisY.logarithmBase,-0.05):Math.max(d.viewPortMax,99*Math.pow(a.axisY.logarithmBase,-0.05)),d.min=u(d.viewPortMin)?1:Math.min(d.viewPortMin,1)):m&&!l?(d.max=u(d.viewPortMax)? +99:Math.max(d.viewPortMax,99),d.min=u(d.viewPortMin)?1:Math.min(d.viewPortMin,1)):m&&l?(d.max=u(d.viewPortMax)?99:Math.max(d.viewPortMax,99),d.min=u(d.viewPortMin)?-99:Math.min(d.viewPortMin,-99)):!m&&l&&(d.max=u(d.viewPortMax)?-1:Math.max(d.viewPortMax,-1),d.min=u(d.viewPortMin)?-99:Math.min(d.viewPortMin,-99));d.viewPortMin=d.min;d.viewPortMax=d.max;a.dataPointYSums=w}};p.prototype._processMultiYPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo, +b=a.axisX.dataInfo,c,e,g,m,l=!1,w=0;wb.max&&(b.max=c);gd.max&&(d.max=m);0B&&(B=1/B),b.minDiff>B&&1!==B&&(b.minDiff=B)):(B=c-h.dataPoints[s-1].x,0>B&&(B*=-1),b.minDiff>B&&0!==B&&(b.minDiff=B)),e&&(null!==e[0]&&h.dataPoints[s-1].y&&null!==h.dataPoints[s-1].y[0])&&(a.axisY.logarithmic?(B=e[0]/ +h.dataPoints[s-1].y[0],1>B&&(B=1/B),d.minDiff>B&&1!==B&&(d.minDiff=B)):(B=e[0]-h.dataPoints[s-1].y[0],0>B&&(B*=-1),d.minDiff>B&&0!==B&&(d.minDiff=B))));if(!(ct&&!n)n=!0;else if(c>t&&n)continue;h.dataPoints[s].label&&(a.axisX.labels[c]=h.dataPoints[s].label);cb.viewPortMax&&(b.viewPortMax=c);if(b.viewPortMin===c&&e)for(p=0;pd.viewPortMax&&(d.viewPortMax=m))}}h.axisX.valueType=h.xValueType=l?"dateTime":"number"}};p.prototype._processSpecificPlotUnit=function(a){if("waterfall"===a.type&&a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,m=0;mb.max&&(b.max=c),l.dataPointEOs[w].cumulativeSumd.max&&(d.max=l.dataPointEOs[w].cumulativeSum),0q&&(q=1/q),b.minDiff>q&&1!==q&&(b.minDiff=q)):(q=c-l.dataPoints[w-1].x,0>q&&(q*=-1),b.minDiff>q&&0!==q&&(b.minDiff=q)),null!==e&&null!==l.dataPoints[w- +1].y&&(a.axisY.logarithmic?(e=l.dataPointEOs[w].cumulativeSum/l.dataPointEOs[w-1].cumulativeSum,1>e&&(e=1/e),d.minDiff>e&&1!==e&&(d.minDiff=e)):(e=l.dataPointEOs[w].cumulativeSum-l.dataPointEOs[w-1].cumulativeSum,0>e&&(e*=-1),d.minDiff>e&&0!==e&&(d.minDiff=e)))),!(cf&&!s)s=!0;else if(c>f&&s)continue;l.dataPoints[w].label&&(a.axisX.labels[c]=l.dataPoints[w].label);cb.viewPortMax&&(b.viewPortMax=c);0d.viewPortMax&&(d.viewPortMax=l.dataPointEOs[w-1].cumulativeSum));l.dataPointEOs[w].cumulativeSumd.viewPortMax&&(d.viewPortMax=l.dataPointEOs[w].cumulativeSum)}l.axisX.valueType=l.xValueType=g?"dateTime":"number"}};p.prototype.calculateAutoBreaks=function(){function a(a,c,b,e){if(e)return b= +Math.pow(Math.min(b*a/c,c/a),0.2),1>=b&&(b=Math.pow(1>a?1/a:Math.min(c/a,a),0.25)),{startValue:a*b,endValue:c/b};b=0.2*Math.min(b-c+a,c-a);0>=b&&(b=0.25*Math.min(c-a,Math.abs(a)));return{startValue:a+b,endValue:c-b}}function d(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){var c=a.axisX.scaleBreaks&&a.axisX.scaleBreaks.autoCalculate&&1<=a.axisX.scaleBreaks.maxNumberOfAutoBreaks,b=a.axisY.scaleBreaks&&a.axisY.scaleBreaks.autoCalculate&&1<=a.axisY.scaleBreaks.maxNumberOfAutoBreaks;if(c|| +b)for(var d=a.axisY.dataInfo,f=a.axisX.dataInfo,g,h=f.min,l=f.max,m=d.min,n=d.max,f=f._dataRanges,d=d._dataRanges,q,w=0,s=0;sk.dataPoints.length))for(w=0;wf[q].max&&(f[q].max=g)),b){var r= +(n+1-m)*Math.max(parseFloat(a.axisY.scaleBreaks.collapsibleThreshold)||10,10)/100;if((g="waterfall"===a.type?k.dataPointEOs[w].cumulativeSum:k.dataPoints[w].y)&&g.length)for(var p=0;pd[q].max&&(d[q].max=g[p]);else u(g)||(q=Math.floor((g-m)/r),gd[q].max&&(d[q].max=g))}}}}function b(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)&&a.axisX.scaleBreaks&&a.axisX.scaleBreaks.autoCalculate&&1<= +a.axisX.scaleBreaks.maxNumberOfAutoBreaks)for(var c=a.axisX.dataInfo,b=c.min,d=c.max,f=c._dataRanges,g,h=0,l=0;lm.dataPoints.length))for(h=0;hf[g].max&&(f[g].max=c)}}for(var c,e=this,g=!1,m=0;m< +this._axes.length;m++)if(this._axes[m].scaleBreaks&&this._axes[m].scaleBreaks.autoCalculate&&1<=this._axes[m].scaleBreaks.maxNumberOfAutoBreaks){g=!0;this._axes[m].dataInfo._dataRanges=[];for(var l=0;l<100/Math.max(parseFloat(this._axes[m].scaleBreaks.collapsibleThreshold)||10,10);l++)this._axes[m].dataInfo._dataRanges.push({min:Infinity,max:-Infinity})}if(g){for(m=0;ms[f].max&&(s[f].max=q)}delete this._axes[m].dataInfo.dataPointYPositiveSums}if(this._axes[m].dataInfo.dataPointYNegativeSums){n= +this._axes[m].dataInfo.dataPointYNegativeSums;s=h;for(l in n)n.hasOwnProperty(l)&&!isNaN(l)&&(q=-1*n[l],u(q)||(f=Math.floor((q-w)/c),qs[f].max&&(s[f].max=q)));delete this._axes[m].dataInfo.dataPointYNegativeSums}for(l=0;lc&&g.push({diff:q,start:s,end:w});break}else l++;if(this._axes[m].scaleBreaks.customBreaks)for(l=0;l=e.x1&&(a<=e.x2&&d>=e.y1&&d<=e.y2)&&(c=e.id)}return c};p.prototype.getAutoFontSize=lb;p.prototype.resetOverlayedCanvas=function(){this.overlaidCanvasCtx.clearRect(0,0,this.width,this.height)};p.prototype.clearCanvas=kb;p.prototype.attachEvent=function(a){this._events.push(a)};p.prototype._touchEventHandler=function(a){if(a.changedTouches&&this.interactivityEnabled){var d=[],b=a.changedTouches,c=b?b[0]:a,e=null;switch(a.type){case "touchstart":case "MSPointerDown":d=["mousemove","mousedown"];this._lastTouchData= +Ra(c);this._lastTouchData.time=new Date;break;case "touchmove":case "MSPointerMove":d=["mousemove"];break;case "touchend":case "MSPointerUp":var g=this._lastTouchData&&this._lastTouchData.time?new Date-this._lastTouchData.time:0,d="touchstart"===this._lastTouchEventType||"MSPointerDown"===this._lastTouchEventType||300>g?["mouseup","click"]:["mouseup"];break;default:return}if(!(b&&1g)this._lastTouchData.scroll=!0}catch(l){}this._lastTouchEventType=a.type;if(this._lastTouchData.scroll&&this.zoomEnabled)this.isDrag&&this.resetOverlayedCanvas(),this.isDrag=!1;else for(b=0;b=e.x1&&d.x<=e.x2&&d.y>=e.y1&&d.y<=e.y2){c[b].call(c.context,d.x,d.y);"mousedown"===b&&!0===c.capture?(p.capturedEventParam=c,this.overlaidCanvas.setCapture?this.overlaidCanvas.setCapture():document.documentElement.addEventListener("mouseup", +this._mouseEventHandler,!1)):"mouseup"===b&&(c.chart.overlaidCanvas.releaseCapture?c.chart.overlaidCanvas.releaseCapture():document.documentElement.removeEventListener("mouseup",this._mouseEventHandler,!1));break}else c=null;a.target.style.cursor=c&&c.cursor?c.cursor:this._defaultCursor}b=this.plotArea;if(d.xb.x2||d.yb.y2)this.toolTip&&this.toolTip.enabled?this.toolTip.hide():this.resetOverlayedCanvas();this.isDrag&&this.zoomEnabled||!this._eventManager||this._eventManager.mouseEventHandler(a)}}; +p.prototype._plotAreaMouseDown=function(a,d){this.isDrag=!0;this.dragStartPoint={x:a,y:d}};p.prototype._plotAreaMouseUp=function(a,d){if(("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)&&this.isDrag){var b=d-this.dragStartPoint.y,c=a-this.dragStartPoint.x,e=0<=this.zoomType.indexOf("x"),g=0<=this.zoomType.indexOf("y"),m=!1;this.resetOverlayedCanvas();if("xySwapped"===this.plotInfo.axisPlacement)var l=g,g=e,e=l;if(this.panEnabled||this.zoomEnabled){if(this.panEnabled)for(e= +g=0;eb.maximum&&(g=b.viewportMaximum/b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum/g,b.sessionVariables.newViewportMaximum=b.viewportMaximum/g,m=!0):b.viewportMinimumb.maximum&&(g=b.viewportMaximum-b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum-g,b.sessionVariables.newViewportMaximum=b.viewportMaximum-g,m=!0);else if((!e||2Math.abs(b)&&(this.panEnabled||this.zoomEnabled)?this.toolTip.hide():this.panEnabled||this.zoomEnabled||this.toolTip.mouseMoveHandler(a, +d);if((!e||2f)var B=f,f=n,n=B;if(q.scaleBreaks)for(B=0;!g&&B=f;if(isFinite(q.dataInfo.minDiff))if(B=q.getApparentDifference(n,f,null,!0),!(g||!(this.panEnabled&&q.scaleBreaks&&q.scaleBreaks._appliedBreaks.length)&&(q.logarithmic&&Bq.maximum))w.push(q),s.push({val1:n,val2:f}),l=!0;else if(!e){l=!1;break}}return{isValid:l,axesWithValidRange:w,axesRanges:s}};p.prototype.preparePlotArea=function(){var a=this.plotArea;!r&&(0b.lineCoordinates.x2?d.x2:b.lineCoordinates.x2;a.y2=d.y2>d.y1?d.y2:b.lineCoordinates.y2;a.width=a.x2-a.x1;a.height=a.y2-a.y1}this.axisY2&&0b.lineCoordinates.x2?d.x2:b.lineCoordinates.x2,a.y2=d.y2>d.y1?d.y2:b.lineCoordinates.y2,a.width=a.x2-a.x1,a.height=a.y2-a.y1)}else d= +this.layoutManager.getFreeSpace(),a.x1=d.x1,a.x2=d.x2,a.y1=d.y1,a.y2=d.y2,a.width=d.width,a.height=d.height;r||(a.canvas.width=a.width,a.canvas.height=a.height,a.canvas.style.left=a.x1+"px",a.canvas.style.top=a.y1+"px",(0b.x2||h.point.yb.y2+1)continue}else if("rangearea"===s||"rangesplinearea"===s){if(h.dataPoint.xy.viewportMaximum||Math.max.apply(null,h.dataPoint.y)A.viewportMaximum)continue}else if(0<=s.indexOf("line")||0<=s.indexOf("area")||0<=s.indexOf("bubble")||0<=s.indexOf("scatter")){if(h.dataPoint.xy.viewportMaximum|| +h.dataPoint.yA.viewportMaximum)continue}else if(0<=s.indexOf("column")||"waterfall"===s||"error"===s&&!h.axisSwapped){if(h.dataPoint.xy.viewportMaximum||h.bounds.y1>b.y2||h.bounds.y2y.viewportMaximum||h.bounds.x1>b.x2||h.bounds.x2 +y.viewportMaximum||Math.max.apply(null,h.dataPoint.y)A.viewportMaximum)continue}else if(h.dataPoint.xy.viewportMaximum)continue;e=m=2;"horizontal"===C?(l=f.width,w=f.height):(w=f.width,l=f.height);if("normal"===this.plotInfo.axisPlacement){if(0<=s.indexOf("line")||0<=s.indexOf("area"))t="auto",m=4;else if(0<=s.indexOf("stacked"))"auto"===t&&(t="inside");else if("bubble"===s||"scatter"===s)t="inside";q=h.point.x- +l/2;"inside"!==t?(e=b.y1,g=b.y2,0h.point.y)):(n=h.point.y+m+c,n>g-w-m-c&&(n="auto"===t?Math.min(h.point.y,g)-w-m-c:g-w-m-c,v=ng-w-m&&("bubble"===s||"scatter"===s)&&(n=Math.min(h.point.y+m,b.y2-w-m))),n=Math.min(n,g-w))}else 0<=s.indexOf("line")||0<=s.indexOf("area")||0<=s.indexOf("scatter")?(t="auto",e=4):0<=s.indexOf("stacked")?"auto"===t&&(t="inside"):"bubble"===s&&(t="inside"),n=h.point.y-w/2,"inside"!==t?(m=b.x1,g=b.x2,0>ma?(q=h.point.x-l-e-c,qh.point.x)):(q=h.point.x+e+c,q>g-l-e-c&&(q="auto"=== +t?Math.min(h.point.x,g)-l-e-c:g-l-e-c,v=qma?Math.max(h.bounds.x1,b.x1)+l/2+e:Math.min(h.bounds.x2,b.x2)-l/2-e:(Math.max(h.bounds.x1,b.x1)+Math.min(h.bounds.x2,b.x2))/2,q=0>ma?Math.max(h.point.x,c)-l/2:Math.min(h.point.x,c)-l/2,q=Math.max(q,m));"vertical"===C&&(n+=w);f.x=q;f.y=n;f.render(!0);p&&("inside"!==t&&(0>s.indexOf("bar")&&("error"!==s||!h.axisSwapped)&&h.point.x>b.x1&&h.point.xs.indexOf("column")&&("error"!==s||h.axisSwapped)&&h.point.y>b.y1&&h.point.y=a.dataSeriesIndexes.length)){var c= +this._eventManager.ghostCtx;b.save();var e=this.plotArea;b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();for(var g=[],m,l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!u)))if("number"!==typeof s[t].y)0s[t].y===a.axisY.reversed?1:-1,color:B})}b.stroke();r&&c.stroke()}}ia.drawMarkers(g);r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&& +b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),c.beginPath());b.restore();b.beginPath();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStepLine=function(a){var d=a.targetCanvasCtx|| +this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx;b.save();var e=this.plotArea;b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();for(var g=[],m,l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!u)))if("number"!==typeof s[t].y)0s[t].y===a.axisY.reversed?1:-1,color:B})}b.stroke();r&&c.stroke()}}ia.drawMarkers(g);r&& +(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),c.beginPath());b.restore();b.beginPath();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation, +easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderSpline=function(a){function d(a){a=v(a,2);if(0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx;c.save();var g=this.plotArea;c.beginPath(); +c.rect(g.x1,g.y1,g.width,g.height);c.clip();for(var m=[],l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!u)))if("number"!==typeof s[p].y)0s[p].y===a.axisY.reversed?1:-1,color:B});u=!1}d(x)}ia.drawMarkers(m);r&&(b.drawImage(this._preRenderCanvas, +0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(g.x1,g.y1,g.width,g.height),e.beginPath());c.restore();c.beginPath();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear, +animationBase:0}}};p.prototype.renderColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,m,l,w,h=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width, +0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0,q=a.axisX.dataInfo.minDiff;isFinite(q)||(q=0.3*Math.abs(a.axisX.range));q=this.dataPointWidth=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(q)/Math.log(a.axisX.range):Math.abs(q)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>s&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(q=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(s=0;sa.axisX.dataInfo.viewPortMax)&&"number"===typeof B[g].y){m=a.axisX.convertValueToPixel(w);l=a.axisY.convertValueToPixel(B[g].y);m=a.axisX.reversed?m+a.plotType.totalDataSeries*q/2-(a.previousDataSeriesCount+s)*q<<0:m-a.plotType.totalDataSeries*q/2+(a.previousDataSeriesCount+s)*q<<0;var k=a.axisX.reversed?m-q<<0:m+q<<0,t;0<=B[g].y?t=h:(t=l,l=h);l>t&&(c=l,l=t,t=c);c=B[g].color?B[g].color:f._colorSet[g%f._colorSet.length];ea(b,m,l,k,t,c,0,null,p&&0<=B[g].y, +0>B[g].y&&p,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,x1:m,y1:l,x2:k,y2:t};c=N(c);r&&ea(this._eventManager.ghostCtx,m,l,k,t,c,0,null,!1,!1,!1,!1);(B[g].indexLabel||f.indexLabel||B[g].indexLabelFormatter||f.indexLabelFormatter)&&this._indexLabels.push({chartType:"column",dataPoint:B[g],dataSeries:f,point:{x:m+(k-m)/2,y:0>B[g].y===a.axisY.reversed?l:t},direction:0>B[g].y===a.axisY.reversed?1:-1,bounds:{x1:m, +y1:Math.min(l,t),x2:k,y2:Math.max(l,t)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore(); +return{source:d,dest:this.plotArea.ctx,animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:ha.axisY.bounds.y2?a.axisY.bounds.y2:h}}};p.prototype.renderStackedColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth? +this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.width<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>s&&(h=Math.min(this.options.dataPointWidth? +this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(f=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&&"number"===typeof t[h].y){s=a.axisX.convertValueToPixel(c);var x=s-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=t[h].y)w[c]=t[h].y+(w[c]?w[c]:0),y=a.axisY.convertValueToPixel(w[c]),q="undefined"!==typeof m[c]?m[c]:n,m[c]=y;else if(q=a.axisY.convertValueToPixel(t[h].y),0<=t[h].y){var A="undefined"!==typeof g[c]?g[c]:0;q-=A;y=n-A;g[c]=A+(y-q)}else A=m[c]?m[c]:0,y=q+A,q=n+A,m[c]=A+(y-q);c=t[h].color?t[h].color:p._colorSet[h%p._colorSet.length];ea(b,x,q,v,y,c,0,null,u&&0<=t[h].y,0>t[h].y&&u,!1, +!1,p.fillOpacity);c=p.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:k,dataPointIndex:h,x1:x,y1:q,x2:v,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,q,v,y,c,0,null,!1,!1,!1,!1);(t[h].indexLabel||p.indexLabel||t[h].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedColumn",dataPoint:t[h],dataSeries:p,point:{x:s,y:0<=t[h].y?q:y},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(q,y),x2:v,y2:Math.max(q, +y)},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx, +animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.y2?a.axisY.bounds.y2:n}}};p.prototype.renderStackedColumn100=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth?this.dataPointMinWidth: +this.options.dataPointWidth?this.dataPointWidth:1;s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.width<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>s&&(h=Math.min(this.options.dataPointWidth? +this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(f=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&&"number"===typeof t[h].y){s=a.axisX.convertValueToPixel(c);q=0!==a.dataPointYSums[c]?100*(t[h].y/a.dataPointYSums[c]):0;var x=s-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=l[c])continue;q=a.axisY.convertValueToPixel(l[c]);y=g[c]?g[c]:n;g[c]=q}else if(a.axisY.scaleBreaks&&0=t[h].y)w[c]=q+("undefined"!==typeof w[c]?w[c]:0),y=a.axisY.convertValueToPixel(w[c]),q=m[c]?m[c]:n,m[c]=y;else if(q=a.axisY.convertValueToPixel(q),0<=t[h].y){var A="undefined"!==typeof g[c]?g[c]:0;q-=A;y=n-A;a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.y1-q)&&(q=e.y1);g[c]=A+(y-q)}else A="undefined"!==typeof m[c]? +m[c]:0,y=q+A,q=n+A,a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.y2-y)&&(y=e.y2),m[c]=A+(y-q);c=t[h].color?t[h].color:k._colorSet[h%k._colorSet.length];ea(b,x,q,v,y,c,0,null,u&&0<=t[h].y,0>t[h].y&&u,!1,!1,k.fillOpacity);c=k.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:h,x1:x,y1:q,x2:v,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,q,v,y,c,0,null,!1,!1,!1,!1);(t[h].indexLabel||k.indexLabel||t[h].indexLabelFormatter||k.indexLabelFormatter)&& +this._indexLabels.push({chartType:"stackedColumn100",dataPoint:t[h],dataSeries:k,point:{x:s,y:0<=t[h].y?q:y},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(q,y),x2:v,y2:Math.max(q,y)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&& +this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.y2?a.axisY.bounds.y2:n}}};p.prototype.renderBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c= +null,e=this.plotArea,g=0,m,l,w,h=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0,q=a.axisX.dataInfo.minDiff;isFinite(q)||(q=0.3*Math.abs(a.axisX.range));q=this.options.dataPointWidth? +this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(q)/Math.log(a.axisX.range):Math.abs(q)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>s&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ss&&(q=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height); +b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(s=0;sa.axisX.dataInfo.viewPortMax)&&"number"===typeof B[g].y){l=a.axisX.convertValueToPixel(w); +m=a.axisY.convertValueToPixel(B[g].y);l=a.axisX.reversed?l+a.plotType.totalDataSeries*q/2-(a.previousDataSeriesCount+s)*q<<0:l-a.plotType.totalDataSeries*q/2+(a.previousDataSeriesCount+s)*q<<0;var p=a.axisX.reversed?l-q<<0:l+q<<0,t;0<=B[g].y?t=h:(t=m,m=h);c=B[g].color?B[g].color:f._colorSet[g%f._colorSet.length];ea(b,t,l,m,p,c,0,null,k,!1,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,x1:t,y1:l,x2:m,y2:p};c= +N(c);r&&ea(this._eventManager.ghostCtx,t,l,m,p,c,0,null,!1,!1,!1,!1);(B[g].indexLabel||f.indexLabel||B[g].indexLabelFormatter||f.indexLabelFormatter)&&this._indexLabels.push({chartType:"bar",dataPoint:B[g],dataSeries:f,point:{x:0<=B[g].y?m:t,y:l+(p-l)/2},direction:0>B[g].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(t,m),y1:l,x2:Math.max(t,m),y2:p},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas, +0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:ha.axisY.bounds.x2?a.axisY.bounds.x2: +h}}};p.prototype.renderStackedBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;q=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.height<< +0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>q&&(h=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,q));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&qq&&(f=q);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&&"number"===typeof t[h].y){q=a.axisX.convertValueToPixel(c);var x=q-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=t[h].y)w[c]=t[h].y+(w[c]?w[c]:0),s=m[c]? +m[c]:n,m[c]=y=a.axisY.convertValueToPixel(w[c]);else if(s=a.axisY.convertValueToPixel(t[h].y),0<=t[h].y){var A=g[c]?g[c]:0;y=n+A;s+=A;g[c]=A+(s-y)}else A=m[c]?m[c]:0,y=s-A,s=n-A,m[c]=A+(s-y);c=t[h].color?t[h].color:k._colorSet[h%k._colorSet.length];ea(b,y,x,s,v,c,0,null,u,!1,!1,!1,k.fillOpacity);c=k.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:h,x1:y,y1:x,x2:s,y2:v};c=N(c);r&&ea(this._eventManager.ghostCtx,y,x,s,v,c,0,null,!1,!1,!1, +!1);(t[h].indexLabel||k.indexLabel||t[h].indexLabelFormatter||k.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar",dataPoint:t[h],dataSeries:k,point:{x:0<=t[h].y?s:y,y:q},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(y,s),y1:x,x2:Math.max(y,s),y2:v},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&& +b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.x2?a.axisY.bounds.x2:n}}};p.prototype.renderStackedBar100=function(a){var d= +a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],m=[],l=[],w=[],h=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),h=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;q=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.height<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)|| +(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&h>q&&(h=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,q));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&qq&&(f=q);b.save();r&&this._eventManager.ghostCtx.save(); +b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;Ba.axisX.dataInfo.viewPortMax)&& +"number"===typeof t[h].y){q=a.axisX.convertValueToPixel(c);var x;x=0!==a.dataPointYSums[c]?100*(t[h].y/a.dataPointYSums[c]):0;var v=q-a.plotType.plotUnits.length*f/2+a.index*f<<0,y=v+f<<0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=l[c])continue;x=g[c]?g[c]:n;g[c]=s=a.axisY.convertValueToPixel(l[c])}else if(a.axisY.scaleBreaks&&0=t[h].y)w[c]=x+(w[c]?w[c]:0),s=m[c]?m[c]: +n,m[c]=x=a.axisY.convertValueToPixel(w[c]);else if(s=a.axisY.convertValueToPixel(x),0<=t[h].y){var A=g[c]?g[c]:0;x=n+A;s+=A;a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.x2-s)&&(s=e.x2);g[c]=A+(s-x)}else A=m[c]?m[c]:0,x=s-A,s=n-A,a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.x1-x)&&(x=e.x1),m[c]=A+(s-x);c=t[h].color?t[h].color:p._colorSet[h%p._colorSet.length];ea(b,x,v,s,y,c,0,null,u,!1,!1,!1,p.fillOpacity);c=p.dataPointIds[h];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:k, +dataPointIndex:h,x1:x,y1:v,x2:s,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,v,s,y,c,0,null,!1,!1,!1,!1);(t[h].indexLabel||p.indexLabel||t[h].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar100",dataPoint:t[h],dataSeries:p,point:{x:0<=t[h].y?s:x,y:q},direction:0>t[h].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(x,s),y1:v,x2:Math.max(x,s),y2:y},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop", +a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:na.axisY.bounds.x2?a.axisY.bounds.x2:n}}};p.prototype.renderArea=function(a){var d,b;function c(){A&&(0=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?y=v:0>a.axisY.viewportMaximum?y=w.y1:0=a.dataSeriesIndexes.length)){var m=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,w=a.axisY.lineCoordinates,h=[],s=this.plotArea,q;g.save();r&&m.save();g.beginPath();g.rect(s.x1,s.y1,s.width,s.height);g.clip();r&&(m.beginPath(),m.rect(s.x1,s.y1,s.width,s.height),m.clip());for(var n=0;na.axisX.dataInfo.viewPortMax&&(!B.connectNullData||!da)))if("number"!==typeof p[k].y)B.connectNullData||(da||d)||c(),da=!0;else{t=a.axisX.convertValueToPixel(x);u=a.axisY.convertValueToPixel(p[k].y);d||da?(!d&&B.connectNullData?(g.setLineDash&&(B.options.nullDataLineDashType||b===B.lineDashType&&B.lineDashType!==B.nullDataLineDashType)&&(d=t,b=u,t=q.x,u=q.y,c(),g.moveTo(q.x,q.y),t=d,u=b,A=q,b=B.nullDataLineDashType,g.setLineDash(Y)),g.lineTo(t,u),r&&m.lineTo(t, +u)):(g.beginPath(),g.moveTo(t,u),r&&(m.beginPath(),m.moveTo(t,u)),A={x:t,y:u}),da=d=!1):(g.lineTo(t,u),r&&m.lineTo(t,u),0==k%250&&c());q={x:t,y:u};kp[k].y===a.axisY.reversed?1:-1,color:z})}c();ia.drawMarkers(h)}}r&&(e.drawImage(this._preRenderCanvas, +0,0,this.width,this.height),g.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&g.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&g.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),g.clearRect(s.x1,s.y1,s.width,s.height),this._eventManager.ghostCtx.restore());g.restore();return{source:e,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear, +animationBase:0}}};p.prototype.renderSplineArea=function(a){function d(){var b=v(x,2);if(0=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum? +t=p:0>a.axisY.viewportMaximum?t=m.y1:0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=a.axisX.lineCoordinates,m=a.axisY.lineCoordinates,l=[],w=this.plotArea;c.save();r&& +e.save();c.beginPath();c.rect(w.x1,w.y1,w.width,w.height);c.clip();r&&(e.beginPath(),e.rect(w.x1,w.y1,w.width,w.height),e.clip());for(var h=0;ha.axisX.dataInfo.viewPortMax&&(!q.connectNullData||!k)))if("number"!==typeof n[f].y)0n[f].y===a.axisY.reversed?1:-1,color:ma});k=!1}d();ia.drawMarkers(l)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(w.x1,w.y1,w.width,w.height), +this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStepArea=function(a){var d,b;function c(){A&&(0=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?y=v:0>a.axisY.viewportMaximum?y=w.y1:0=a.dataSeriesIndexes.length)){var m=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,w=a.axisY.lineCoordinates,h=[],s=this.plotArea,q;g.save();r&&m.save();g.beginPath();g.rect(s.x1,s.y1,s.width,s.height);g.clip();r&&(m.beginPath(),m.rect(s.x1,s.y1,s.width,s.height),m.clip());for(var n=0;na.axisX.dataInfo.viewPortMax&&(!B.connectNullData||!b))){var Z=u;"number"!==typeof k[p].y?(B.connectNullData||(b||d)||c(),b=!0):(t=a.axisX.convertValueToPixel(x),u=a.axisY.convertValueToPixel(k[p].y),d||b?(!d&&B.connectNullData?(g.setLineDash&&(B.options.nullDataLineDashType||Y===B.lineDashType&&B.lineDashType!==B.nullDataLineDashType)&&(d= +t,b=u,t=q.x,u=q.y,c(),g.moveTo(q.x,q.y),t=d,u=b,A=q,Y=B.nullDataLineDashType,g.setLineDash(ca)),g.lineTo(t,Z),g.lineTo(t,u),r&&(m.lineTo(t,Z),m.lineTo(t,u))):(g.beginPath(),g.moveTo(t,u),r&&(m.beginPath(),m.moveTo(t,u)),A={x:t,y:u}),b=d=!1):(g.lineTo(t,Z),r&&m.lineTo(t,Z),g.lineTo(t,u),r&&m.lineTo(t,u),0==p%250&&c()),q={x:t,y:u},pk[p].y===a.axisY.reversed?1:-1,color:z}))}c();ia.drawMarkers(h)}}r&&(e.drawImage(this._preRenderCanvas,0,0,this.width,this.height),g.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&g.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&g.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas, +0,0,this.width,this.height),g.clearRect(s.x1,s.y1,s.width,s.height),this._eventManager.ghostCtx.restore());g.restore();return{source:e,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStackedArea=function(a){function d(){if(!(1>h.length)){for(0=a.dataSeriesIndexes.length)){var e=null,g=null,m=[],l=this.plotArea,w=[],h=[],s=[],q=[],n=0,f,k,p=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),u=this._eventManager.ghostCtx,t,C,x;r&&u.beginPath();c.save();r&&u.save();c.beginPath();c.rect(l.x1,l.y1,l.width,l.height);c.clip();r&&(u.beginPath(),u.rect(l.x1,l.y1,l.width,l.height),u.clip());for(var e=[],v=0;va.axisX.dataInfo.viewPortMax&&(!A.connectNullData||!da)))if("number"!==typeof Z.y)A.connectNullData||(da||C)||d(),da=!0;else{f=a.axisX.convertValueToPixel(g);var oa= +w[g]?w[g]:0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=q[g]&&a.axisY.logarithmic)continue;k=a.axisY.convertValueToPixel(q[g])}else k=a.axisY.convertValueToPixel(Z.y),k-=oa;h.push({x:f,y:p-oa});w[g]=p-k;C||da?(!C&&A.connectNullData?(c.setLineDash&&(A.options.nullDataLineDashType||x===A.lineDashType&&A.lineDashType!==A.nullDataLineDashType)&&(C=h.pop(),x=h[h.length-1],d(),c.moveTo(t.x,t.y),h.push(x),h.push(C),x=A.nullDataLineDashType, +c.setLineDash(Y)),c.lineTo(f,k),r&&u.lineTo(f,k)):(c.beginPath(),c.moveTo(f,k),r&&(u.beginPath(),u.moveTo(f,k))),da=C=!1):(c.lineTo(f,k),r&&u.lineTo(f,k),0==n%250&&(d(),c.moveTo(f,k),r&&u.moveTo(f,k),h.push({x:f,y:p-oa})));t={x:f,y:k};nz[n].y===a.axisY.reversed?1:-1,color:e})}}d();c.moveTo(f,k);r&&u.moveTo(f,k)}delete A.dataPointIndexes}ia.drawMarkers(m);r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&& +c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(l.x1,l.y1,l.width,l.height),u.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStackedArea100=function(a){function d(){for(0=a.dataSeriesIndexes.length)){var e=null,g=null,m=this.plotArea,l=[],w=[],h=[],s=[],q=[],n=0,f,k,p,u,t,C=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),x=this._eventManager.ghostCtx;c.save();r&&x.save();c.beginPath();c.rect(m.x1,m.y1,m.width,m.height);c.clip();r&&(x.beginPath(), +x.rect(m.x1,m.y1,m.width,m.height),x.clip());for(var e=[],v=0;va.axisX.dataInfo.viewPortMax&&(!A.connectNullData|| +!da)))if("number"!==typeof Z.y)A.connectNullData||(da||u)||d(),da=!0;else{var oa;oa=0!==a.dataPointYSums[g]?100*(Z.y/a.dataPointYSums[g]):0;f=a.axisX.convertValueToPixel(g);var la=w[g]?w[g]:0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0=q[g]&&a.axisY.logarithmic)continue;k=a.axisY.convertValueToPixel(q[g])}else k=a.axisY.convertValueToPixel(oa),k-=la;h.push({x:f,y:C-la});w[g]=C-k;u||da?(!u&&A.connectNullData?(c.setLineDash&& +(A.options.nullDataLineDashType||t===A.lineDashType&&A.lineDashType!==A.nullDataLineDashType)&&(u=h.pop(),t=h[h.length-1],d(),c.moveTo(p.x,p.y),h.push(t),h.push(u),t=A.nullDataLineDashType,c.setLineDash(Y)),c.lineTo(f,k),r&&x.lineTo(f,k)):(c.beginPath(),c.moveTo(f,k),r&&(x.beginPath(),x.moveTo(f,k))),da=u=!1):(c.lineTo(f,k),r&&x.lineTo(f,k),0==n%250&&(d(),c.moveTo(f,k),r&&x.moveTo(f,k),h.push({x:f,y:C-la})));p={x:f,y:k};nz[n].y===a.axisY.reversed?1:-1,color:e})}}d();c.moveTo(f,k);r&&x.moveTo(f,k)}delete A.dataPointIndexes}ia.drawMarkers(l);r&&(b.drawImage(this._preRenderCanvas,0, +0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(m.x1,m.y1,m.width,m.height),x.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}}; +p.prototype.renderBubble=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this.plotArea,e=0,g,m;b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(c.x1,c.y1,c.width,c.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.clip());for(var l=-Infinity,w=Infinity,h=0;ha.axisX.dataInfo.viewPortMax||"undefined"===typeof n[e].z||(f=n[e].z,f>l&&(l=f),fa.axisX.dataInfo.viewPortMax)&&"number"===typeof n[e].y){g=a.axisX.convertValueToPixel(g);m=a.axisY.convertValueToPixel(n[e].y);var f=n[e].z,u=2*Math.max(Math.sqrt((l===w?p/2:k+(p-k)/(l-w)*(f-w))/Math.PI)<<0,1),f=q.getMarkerProperties(e,b);f.size=u;b.globalAlpha=q.fillOpacity;ia.drawMarker(g,m,b,f.type,f.size,f.color,f.borderColor,f.borderThickness);b.globalAlpha=1;var t=q.dataPointIds[e];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:s, +dataPointIndex:e,x1:g,y1:m,size:u};u=N(t);r&&ia.drawMarker(g,m,this._eventManager.ghostCtx,f.type,f.size,u,u,f.borderThickness);(n[e].indexLabel||q.indexLabel||n[e].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"bubble",dataPoint:n[e],dataSeries:q,point:{x:g,y:m},direction:1,bounds:{x1:g-f.size/2,y1:m-f.size/2,x2:g+f.size/2,y2:m+f.size/2},color:null})}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&& +b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderScatter=function(a){var d= +a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this.plotArea,e=0,g,m;b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(c.x1,c.y1,c.width,c.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.clip());for(var l=0;la.axisX.dataInfo.viewPortMax)&&"number"===typeof s[e].y){g=a.axisX.convertValueToPixel(g);m=a.axisY.convertValueToPixel(s[e].y);var f=h.getMarkerProperties(e,g,m,b);b.globalAlpha=h.fillOpacity;ia.drawMarker(f.x,f.y,f.ctx,f.type,f.size,f.color,f.borderColor,f.borderThickness);b.globalAlpha=1;Math.sqrt((q-g)*(q-g)+(n-m)*(n-m))Math.min(this.plotArea.width,this.plotArea.height)||(q=h.dataPointIds[e],this._eventManager.objectMap[q]={id:q,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:e,x1:g,y1:m},q=N(q),r&&ia.drawMarker(f.x,f.y,this._eventManager.ghostCtx,f.type,f.size,q,q,f.borderThickness),(s[e].indexLabel||h.indexLabel||s[e].indexLabelFormatter||h.indexLabelFormatter)&&this._indexLabels.push({chartType:"scatter",dataPoint:s[e],dataSeries:h,point:{x:g,y:m},direction:1,bounds:{x1:g-f.size/2,y1:m-f.size/ +2,x2:g+f.size/2,y2:m+f.size/2},color:null}),q=g,n=m)}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.restore()); +b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderCandlestick=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=null,m=this.plotArea,l=0,w,h,s,q,n,f,e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,g=this.options.dataPointMaxWidth? +this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.015*this.width,k=a.axisX.dataInfo.minDiff;isFinite(k)||(k=0.3*Math.abs(a.axisX.range));k=this.options.dataPointWidth?this.dataPointWidth:0.7*m.width*(a.axisX.logarithmic?Math.log(k)/Math.log(a.axisX.range):Math.abs(k)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&&e>g&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,g));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&gg&&(k=g);b.save();r&&c.save();b.beginPath();b.rect(m.x1,m.y1,m.width,m.height);b.clip();r&&(c.beginPath(),c.rect(m.x1,m.y1,m.width,m.height),c.clip());for(var p=0;pa.axisX.dataInfo.viewPortMax)&&!u(C[l].y)&&C[l].y.length&& +"number"===typeof C[l].y[0]&&"number"===typeof C[l].y[1]&&"number"===typeof C[l].y[2]&&"number"===typeof C[l].y[3]){w=a.axisX.convertValueToPixel(f);h=a.axisY.convertValueToPixel(C[l].y[0]);s=a.axisY.convertValueToPixel(C[l].y[1]);q=a.axisY.convertValueToPixel(C[l].y[2]);n=a.axisY.convertValueToPixel(C[l].y[3]);var z=w-k/2<<0,y=z+k<<0,g=t.options.fallingColor?t.fallingColor:t._colorSet[0],e=C[l].color?C[l].color:t._colorSet[0],A=Math.round(Math.max(1,0.15*k)),D=0===A%2?0:0.5,aa=t.dataPointIds[l]; +this._eventManager.objectMap[aa]={id:aa,objectType:"dataPoint",dataSeriesIndex:v,dataPointIndex:l,x1:z,y1:h,x2:y,y2:s,x3:w,y3:q,x4:w,y4:n,borderThickness:A,color:e};b.strokeStyle=e;b.beginPath();b.lineWidth=A;c.lineWidth=Math.max(A,4);"candlestick"===t.type?(b.moveTo(w-D,s),b.lineTo(w-D,Math.min(h,n)),b.stroke(),b.moveTo(w-D,Math.max(h,n)),b.lineTo(w-D,q),b.stroke(),ea(b,z,Math.min(h,n),y,Math.max(h,n),C[l].y[0]<=C[l].y[3]?t.risingColor:g,A,e,x,x,!1,!1,t.fillOpacity),r&&(e=N(aa),c.strokeStyle=e,c.moveTo(w- +D,s),c.lineTo(w-D,Math.min(h,n)),c.stroke(),c.moveTo(w-D,Math.max(h,n)),c.lineTo(w-D,q),c.stroke(),ea(c,z,Math.min(h,n),y,Math.max(h,n),e,0,null,!1,!1,!1,!1))):"ohlc"===t.type&&(b.moveTo(w-D,s),b.lineTo(w-D,q),b.stroke(),b.beginPath(),b.moveTo(w,h),b.lineTo(z,h),b.stroke(),b.beginPath(),b.moveTo(w,n),b.lineTo(y,n),b.stroke(),r&&(e=N(aa),c.strokeStyle=e,c.moveTo(w-D,s),c.lineTo(w-D,q),c.stroke(),c.beginPath(),c.moveTo(w,h),c.lineTo(z,h),c.stroke(),c.beginPath(),c.moveTo(w,n),c.lineTo(y,n),c.stroke())); +(C[l].indexLabel||t.indexLabel||C[l].indexLabelFormatter||t.indexLabelFormatter)&&this._indexLabels.push({chartType:t.type,dataPoint:C[l],dataSeries:t,point:{x:z+(y-z)/2,y:a.axisY.reversed?q:s},direction:1,bounds:{x1:z,y1:Math.min(s,q),x2:y,y2:Math.max(s,q)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas, +0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(m.x1,m.y1,m.width,m.height),c.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderBoxAndWhisker=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var e= +null,g=this.plotArea,m=0,l,w,h,s,q,n,f,e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,m=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.015*this.width,k=a.axisX.dataInfo.minDiff;isFinite(k)||(k=0.3*Math.abs(a.axisX.range));k=this.options.dataPointWidth?this.dataPointWidth:0.7*g.width*(a.axisX.logarithmic?Math.log(k)/Math.log(a.axisX.range):Math.abs(k)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&& +e>m&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&mm&&(k=m);b.save();r&&c.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();r&&(c.beginPath(),c.rect(g.x1,g.y1,g.width,g.height),c.clip());for(var p=!1,p=!!a.axisY.reversed,v=0;va.axisX.dataInfo.viewPortMax)&&!u(x[m].y)&&x[m].y.length&&"number"===typeof x[m].y[0]&&"number"===typeof x[m].y[1]&&"number"===typeof x[m].y[2]&&"number"===typeof x[m].y[3]&&"number"===typeof x[m].y[4]&&5===x[m].y.length){l=a.axisX.convertValueToPixel(f);w=a.axisY.convertValueToPixel(x[m].y[0]);h=a.axisY.convertValueToPixel(x[m].y[1]);s=a.axisY.convertValueToPixel(x[m].y[2]); +q=a.axisY.convertValueToPixel(x[m].y[3]);n=a.axisY.convertValueToPixel(x[m].y[4]);var y=l-k/2<<0,A=l+k/2<<0,e=x[m].color?x[m].color:C._colorSet[0],D=Math.round(Math.max(1,0.15*k)),aa=0===D%2?0:0.5,T=x[m].whiskerColor?x[m].whiskerColor:x[m].color?C.whiskerColor?C.whiskerColor:x[m].color:C.whiskerColor?C.whiskerColor:e,Y="number"===typeof x[m].whiskerThickness?x[m].whiskerThickness:"number"===typeof C.options.whiskerThickness?C.whiskerThickness:D,ca=x[m].whiskerDashType?x[m].whiskerDashType:C.whiskerDashType, +da=u(x[m].whiskerLength)?u(C.options.whiskerLength)?k:C.whiskerLength:x[m].whiskerLength,da="number"===typeof da?0>=da?0:da>=k?k:da:"string"===typeof da?parseInt(da)*k/100>k?k:parseInt(da)*k/100:k,Z=1===Math.round(Y)%2?0.5:0,oa=x[m].stemColor?x[m].stemColor:x[m].color?C.stemColor?C.stemColor:x[m].color:C.stemColor?C.stemColor:e,la="number"===typeof x[m].stemThickness?x[m].stemThickness:"number"===typeof C.options.stemThickness?C.stemThickness:D,G=1===Math.round(la)%2?0.5:0,F=x[m].stemDashType?x[m].stemDashType: +C.stemDashType,E=x[m].lineColor?x[m].lineColor:x[m].color?C.lineColor?C.lineColor:x[m].color:C.lineColor?C.lineColor:e,H="number"===typeof x[m].lineThickness?x[m].lineThickness:"number"===typeof C.options.lineThickness?C.lineThickness:D,I=x[m].lineDashType?x[m].lineDashType:C.lineDashType,K=1===Math.round(H)%2?0.5:0,L=C.upperBoxColor,O=C.lowerBoxColor,Q=u(C.options.fillOpacity)?1:C.fillOpacity,P=C.dataPointIds[m];this._eventManager.objectMap[P]={id:P,objectType:"dataPoint",dataSeriesIndex:t,dataPointIndex:m, +x1:y,y1:w,x2:A,y2:h,x3:l,y3:s,x4:l,y4:q,y5:n,borderThickness:D,color:e,stemThickness:la,stemColor:oa,whiskerThickness:Y,whiskerLength:da,whiskerColor:T,lineThickness:H,lineColor:E};b.save();0=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,m,l,w,g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth: +1;m=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.03*this.width;var h=a.axisX.dataInfo.minDiff;isFinite(h)||(h=0.3*Math.abs(a.axisX.range));h=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(h)/Math.log(a.axisX.range):Math.abs(h)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>m&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&& +(this.dataPointMinWidth&&mm&&(h=m);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var s=0;sa.axisX.dataInfo.viewPortMax)&&!u(f[g].y)&&f[g].y.length&&"number"===typeof f[g].y[0]&&"number"===typeof f[g].y[1]){c=a.axisX.convertValueToPixel(w);m=a.axisY.convertValueToPixel(f[g].y[0]);l=a.axisY.convertValueToPixel(f[g].y[1]);var p=a.axisX.reversed?c+a.plotType.totalDataSeries*h/2-(a.previousDataSeriesCount+s)*h<<0:c-a.plotType.totalDataSeries*h/2+(a.previousDataSeriesCount+ +s)*h<<0,v=a.axisX.reversed?p-h<<0:p+h<<0,c=f[g].color?f[g].color:n._colorSet[g%n._colorSet.length];if(m>l){var t=m;m=l;l=t}t=n.dataPointIds[g];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:q,dataPointIndex:g,x1:p,y1:m,x2:v,y2:l};ea(b,p,m,v,l,c,0,c,k,k,!1,!1,n.fillOpacity);c=N(t);r&&ea(this._eventManager.ghostCtx,p,m,v,l,c,0,null,!1,!1,!1,!1);if(f[g].indexLabel||n.indexLabel||f[g].indexLabelFormatter||n.indexLabelFormatter)this._indexLabels.push({chartType:"rangeColumn", +dataPoint:f[g],dataSeries:n,indexKeyword:0,point:{x:p+(v-p)/2,y:f[g].y[1]>=f[g].y[0]?l:m},direction:f[g].y[1]>=f[g].y[0]?-1:1,bounds:{x1:p,y1:Math.min(m,l),x2:v,y2:Math.max(m,l)},color:c}),this._indexLabels.push({chartType:"rangeColumn",dataPoint:f[g],dataSeries:n,indexKeyword:1,point:{x:p+(v-p)/2,y:f[g].y[1]>=f[g].y[0]?m:l},direction:f[g].y[1]>=f[g].y[0]?1:-1,bounds:{x1:p,y1:Math.min(m,l),x2:v,y2:Math.max(m,l)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation= +"source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderError= +function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=a.axisY._position?"left"===a.axisY._position||"right"===a.axisY._position?!1:!0:!1;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=!1,m=this.plotArea,l=0,w,h,s,q,n,f,k,p=a.axisX.dataInfo.minDiff;isFinite(p)||(p=0.3*Math.abs(a.axisX.range));b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(m.x1,m.y1,m.width,m.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(m.x1, +m.y1,m.width,m.height),this._eventManager.ghostCtx.clip());for(var v=0,t=0;tl&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,l));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&ll&&(t=l);if(0=T.length?0:T.length>=t?t:T.length:"string"===typeof T.length?parseInt(T.length)*t/100>t?t:parseInt(T.length)*t/100>t:t;T.thickness="number"===typeof T.thickness?0>T.thickness?0:Math.round(T.thickness):2;var Y={color:y[l].stemColor?y[l].stemColor:y[l].color?z.stemColor?z.stemColor:y[l].color:z.stemColor?z.stemColor:e,thickness:y[l].stemThickness?y[l].stemThickness:z.stemThickness,dashType:y[l].stemDashType? +y[l].stemDashType:z.stemDashType};Y.thickness="number"===typeof Y.thickness?0>Y.thickness?0:Math.round(Y.thickness):2;y[l].getTime?k=y[l].x.getTime():k=y[l].x;if(!(ka.axisX.dataInfo.viewPortMax)&&!u(y[l].y)&&y[l].y.length&&"number"===typeof y[l].y[0]&&"number"===typeof y[l].y[1]){var ca=a.axisX.convertValueToPixel(k);c?h=ca:w=ca;ca=a.axisY.convertValueToPixel(y[l].y[0]);c?s=ca:n=ca;ca=a.axisY.convertValueToPixel(y[l].y[1]);c?q=ca:f=ca;c?(n=a.axisX.reversed?h+(A?v: +1)*t/2-(A?D-1:0)*t<<0:h-(A?v:1)*t/2+(A?D-1:0)*t<<0,f=a.axisX.reversed?n-t<<0:n+t<<0):(s=a.axisX.reversed?w+(A?v:1)*t/2-(A?D-1:0)*t<<0:w-(A?v:1)*t/2+(A?D-1:0)*t<<0,q=a.axisX.reversed?s-t<<0:s+t<<0);!c&&n>f&&(ca=n,n=f,f=ca);c&&s>q&&(ca=s,s=q,q=ca);ca=z.dataPointIds[l];this._eventManager.objectMap[ca]={id:ca,objectType:"dataPoint",dataSeriesIndex:x,dataPointIndex:l,x1:Math.min(s,q),y1:Math.min(n,f),x2:Math.max(q,s),y2:Math.max(f,n),isXYSwapped:c,stemProperties:Y,whiskerProperties:T};E(b,Math.min(s,q), +Math.min(n,f),Math.max(q,s),Math.max(f,n),e,T,Y,c);r&&E(this._eventManager.ghostCtx,s,n,q,f,e,T,Y,c);if(y[l].indexLabel||z.indexLabel||y[l].indexLabelFormatter||z.indexLabelFormatter)this._indexLabels.push({chartType:"error",dataPoint:y[l],dataSeries:z,indexKeyword:0,point:{x:c?y[l].y[1]>=y[l].y[0]?s:q:s+(q-s)/2,y:c?n+(f-n)/2:y[l].y[1]>=y[l].y[0]?f:n},direction:y[l].y[1]>=y[l].y[0]?-1:1,bounds:{x1:c?Math.min(s,q):s,y1:c?n:Math.min(n,f),x2:c?Math.max(s,q):q,y2:c?f:Math.max(n,f)},color:e,axisSwapped:c}), +this._indexLabels.push({chartType:"error",dataPoint:y[l],dataSeries:z,indexKeyword:1,point:{x:c?y[l].y[1]>=y[l].y[0]?q:s:s+(q-s)/2,y:c?n+(f-n)/2:y[l].y[1]>=y[l].y[0]?n:f},direction:y[l].y[1]>=y[l].y[0]?1:-1,bounds:{x1:c?Math.min(s,q):s,y1:c?n:Math.min(n,f),x2:c?Math.max(s,q):q,y2:c?f:Math.max(n,f)},color:e,axisSwapped:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height), +a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(m.x1,m.y1,m.width,m.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx: +d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,m,l,w,h,g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;m=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0;var s=a.axisX.dataInfo.minDiff;isFinite(s)||(s=0.3*Math.abs(a.axisX.range));s=this.options.dataPointWidth?this.dataPointWidth:0.9* +(e.height*(a.axisX.logarithmic?Math.log(s)/Math.log(a.axisX.range):Math.abs(s)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>m&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&mm&&(s=m);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(), +this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var q=0;qa.axisX.dataInfo.viewPortMax)&&!u(k[g].y)&&k[g].y.length&&"number"===typeof k[g].y[0]&&"number"===typeof k[g].y[1]){m=a.axisY.convertValueToPixel(k[g].y[0]); +l=a.axisY.convertValueToPixel(k[g].y[1]);w=a.axisX.convertValueToPixel(h);w=a.axisX.reversed?w+a.plotType.totalDataSeries*s/2-(a.previousDataSeriesCount+q)*s<<0:w-a.plotType.totalDataSeries*s/2+(a.previousDataSeriesCount+q)*s<<0;var v=a.axisX.reversed?w-s<<0:w+s<<0;m>l&&(c=m,m=l,l=c);c=k[g].color?k[g].color:f._colorSet[g%f._colorSet.length];ea(b,m,w,l,v,c,0,null,p,!1,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g, +x1:m,y1:w,x2:l,y2:v};c=N(c);r&&ea(this._eventManager.ghostCtx,m,w,l,v,c,0,null,!1,!1,!1,!1);if(k[g].indexLabel||f.indexLabel||k[g].indexLabelFormatter||f.indexLabelFormatter)this._indexLabels.push({chartType:"rangeBar",dataPoint:k[g],dataSeries:f,indexKeyword:0,point:{x:k[g].y[1]>=k[g].y[0]?m:l,y:w+(v-w)/2},direction:k[g].y[1]>=k[g].y[0]?-1:1,bounds:{x1:Math.min(m,l),y1:w,x2:Math.max(m,l),y2:v},color:c}),this._indexLabels.push({chartType:"rangeBar",dataPoint:k[g],dataSeries:f,indexKeyword:1,point:{x:k[g].y[1]>= +k[g].y[0]?l:m,y:w+(v-w)/2},direction:k[g].y[1]>=k[g].y[0]?1:-1,bounds:{x1:Math.min(m,l),y1:w,x2:Math.max(m,l),y2:v},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1, +e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeArea=function(a){function d(){if(C){var a=null;0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=[],m=this.plotArea;c.save();r&&e.save();c.beginPath();c.rect(m.x1,m.y1,m.width,m.height);c.clip();r&&(e.beginPath(),e.rect(m.x1,m.y1,m.width,m.height),e.clip());for(var l=0;la.axisX.dataInfo.viewPortMax&&(!s.connectNullData||!T)))if(null!==q[f].y&&q[f].y.length&&"number"===typeof q[f].y[0]&&"number"===typeof q[f].y[1]){k=a.axisX.convertValueToPixel(t);p=a.axisY.convertValueToPixel(q[f].y[0]);u=a.axisY.convertValueToPixel(q[f].y[1]);n||T?(s.connectNullData&&!n?(c.setLineDash&&(s.options.nullDataLineDashType||A===s.lineDashType&&s.lineDashType!==s.nullDataLineDashType)&&(w[w.length- +1].newLineDashArray=D,A=s.nullDataLineDashType,c.setLineDash(z)),c.lineTo(k,p),r&&e.lineTo(k,p),w.push({x:k,y:u})):(c.beginPath(),c.moveTo(k,p),C={x:k,y:p},w=[],w.push({x:k,y:u}),r&&(e.beginPath(),e.moveTo(k,p))),T=n=!1):(c.lineTo(k,p),w.push({x:k,y:u}),r&&e.lineTo(k,p),0==f%250&&d());t=s.dataPointIds[f];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:f,x1:k,y1:p,y2:u};fq[f].y[1]===a.axisY.reversed?-1:1,color:x}),this._indexLabels.push({chartType:"rangeArea",dataPoint:q[f],dataSeries:s,indexKeyword:1,point:{x:k, +y:u},direction:q[f].y[0]>q[f].y[1]===a.axisY.reversed?1:-1,color:x})}else T||n||d(),T=!0;d();ia.drawMarkers(g)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(m.x1,m.y1, +m.width,m.height),this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderRangeSplineArea=function(a){function d(a,b){var d=v(u,2);if(0=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=[],m=this.plotArea;c.save();r&&e.save();c.beginPath();c.rect(m.x1,m.y1,m.width,m.height);c.clip();r&&(e.beginPath(),e.rect(m.x1,m.y1,m.width, +m.height),e.clip());for(var l=0;la.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!f)))if(null!==k[q].y&&k[q].y.length&&"number"===typeof k[q].y[0]&&"number"===typeof k[q].y[1]){n=a.axisX.convertValueToPixel(n);f=a.axisY.convertValueToPixel(k[q].y[0]);p=a.axisY.convertValueToPixel(k[q].y[1]);var E=h.dataPointIds[q];this._eventManager.objectMap[E]={id:E,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:q, +x1:n,y1:f,y2:p};u[u.length]={x:n,y:f};z[z.length]={x:n,y:p};q=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx,e=null,g=this.plotArea,m=0,l,k,h,s,q=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),m=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;k=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width,0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0;var n= +a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.options.dataPointWidth?this.dataPointWidth:0.6*(g.width*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&m>k&&(m=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,k));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&kk&&(n=k);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(g.x1,g.y1,g.width,g.height),this._eventManager.ghostCtx.clip());for(var f=0;fh&&(e=k,k=h,h=e);a.axisY.reversed&&(e=k,k=h,h=e);e=u.dataPointIds[m];this._eventManager.objectMap[e]={id:e,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:m,x1:l,y1:k,x2:F,y2:h}; +var T=v[m].color?v[m].color:0v[m].y===a.axisY.reversed?1:-1,bounds:{x1:l,y1:Math.min(k,h),x2:F,y2:Math.max(k,h)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height), +b.clearRect(g.x1,g.y1,g.width,g.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};var ja=function(a,d,b,c,e,g,m,l,k){if(!(0>b)){"undefined"===typeof l&&(l=1);if(!r){var h=Number((m%(2*Math.PI)).toFixed(8));Number((g%(2*Math.PI)).toFixed(8))===h&&(m-=1E-4)}a.save();a.globalAlpha=l;"pie"===e?(a.beginPath(),a.moveTo(d.x,d.y),a.arc(d.x,d.y,b,g,m,!1),a.fillStyle=c,a.strokeStyle= +"white",a.lineWidth=2,a.closePath(),a.fill()):"doughnut"===e&&(a.beginPath(),a.arc(d.x,d.y,b,g,m,!1),0<=k&&a.arc(d.x,d.y,k*b,m,g,!0),a.closePath(),a.fillStyle=c,a.strokeStyle="white",a.lineWidth=2,a.fill());a.globalAlpha=1;a.restore()}};p.prototype.renderPie=function(a){function d(){if(h&&s){for(var a=0,b=0,c=0,e=0,d=0;dMath.PI/2-t&&m.midAngle +m.midAngle)c=d;a++}else if(m.midAngle>3*Math.PI/2-t&&m.midAngle<3*Math.PI/2+t){if(0===b||f[e].midAngle>m.midAngle)e=d;b++}m.hemisphere=g>Math.PI/2&&g<=3*Math.PI/2?"left":"right";m.indexLabelTextBlock=new ka(k.plotArea.ctx,{fontSize:m.indexLabelFontSize,fontFamily:m.indexLabelFontFamily,fontColor:m.indexLabelFontColor,fontStyle:m.indexLabelFontStyle,fontWeight:m.indexLabelFontWeight,horizontalAlign:"left",backgroundColor:m.indexLabelBackgroundColor,maxWidth:m.indexLabelMaxWidth,maxHeight:m.indexLabelWrap? +5*m.indexLabelFontSize:1.5*m.indexLabelFontSize,text:m.indexLabelText,padding:0,textBaseline:"top"});m.indexLabelTextBlock.measureText()}l=g=0;q=!1;for(d=0;dMath.PI/2-t&&m.midAngle3*Math.PI/2-t&&m.midAngle<3*Math.PI/2+t)&&(l<=b/2&&!q?(m.hemisphere="left",l++):(m.hemisphere="right",q=!0))}}function b(a){var b= +k.plotArea.ctx;b.clearRect(n.x1,n.y1,n.width,n.height);b.fillStyle=k.backgroundColor;b.fillRect(n.x1,n.y1,n.width,n.height);for(b=0;bc){var d=0.07*A*Math.cos(f[b].midAngle),g=0.07*A*Math.sin(f[b].midAngle),m=!1;if(s[b].exploded){if(1E-9a.indexLabelTextBlock.y?d-e:c-f}function e(a){for(var b=null,e=1;ec(f[b],f[a])||("right"===f[a].hemisphere?f[b].indexLabelTextBlock.y>=f[a].indexLabelTextBlock.y:f[b].indexLabelTextBlock.y<=f[a].indexLabelTextBlock.y)))break;else b=null;return b}function g(a,b,d){d=(d||0)+1;if(1E3< +d)return 0;b=b||0;var m=0,h=x.y-1*r,l=x.y+1*r;if(0<=a&&ab&&n.indexLabelTextBlock.yl)return 0;var k=0,q=0,q=k=k=0;0>b?n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2>h&&n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2+bl&&(b=n.indexLabelTextBlock.y+ +n.indexLabelTextBlock.height/2+b-l);b=n.indexLabelTextBlock.y+b;h=0;h="right"===n.hemisphere?x.x+Math.sqrt(Math.pow(r,2)-Math.pow(b-x.y,2)):x.x-Math.sqrt(Math.pow(r,2)-Math.pow(b-x.y,2));q=x.x+A*Math.cos(n.midAngle);k=x.y+A*Math.sin(n.midAngle);k=Math.sqrt(Math.pow(h-q,2)+Math.pow(b-k,2));q=Math.acos(A/r);k=Math.acos((r*r+A*A-k*k)/(2*A*r));b=kc(f[h],f[a])||("right"===f[a].hemisphere?f[h].indexLabelTextBlock.y<=f[a].indexLabelTextBlock.y:f[h].indexLabelTextBlock.y>=f[a].indexLabelTextBlock.y)))break;else h=null;q=h;k=e(a);l=h=0;0>b?(l="right"===n.hemisphere?q:k,m=b,null!==l&&(q=-b,b=n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2-(f[l].indexLabelTextBlock.y+f[l].indexLabelTextBlock.height/2),b-q+h.toFixed(C)&&(m=b>p?-(b-p):-(q-(l-h)))))):0p?b-p:q-(h-l)))));m&&(d=n.indexLabelTextBlock.y+m,b=0,b="right"===n.hemisphere?x.x+Math.sqrt(Math.pow(r,2)-Math.pow(d-x.y,2)):x.x-Math.sqrt(Math.pow(r,2)-Math.pow(d-x.y,2)),n.midAngle>Math.PI/2-t&&n.midAngleh.indexLabelTextBlock.x?b=h.indexLabelTextBlock.x-15:"right"===n.hemisphere&&("left"===a.hemisphere&&b3*Math.PI/2-t&&n.midAngle<3*Math.PI/2+t&&(h=(a-1+f.length)%f.length,h=f[h],a=f[(a+1+f.length)%f.length],"right"===n.hemisphere&&"left"===h.hemisphere&&ba.indexLabelTextBlock.x)&&(b=a.indexLabelTextBlock.x- +15)),n.indexLabelTextBlock.y=d,n.indexLabelTextBlock.x=b,n.indexLabelAngle=Math.atan2(n.indexLabelTextBlock.y-x.y,n.indexLabelTextBlock.x-x.x))}return m}function m(){var a=k.plotArea.ctx;a.fillStyle="grey";a.strokeStyle="grey";a.font="16px Arial";a.textBaseline="middle";for(var b=a=0,d=0,m=!0,b=0;10>b&&(1>b||0z){for(var E=u=0,H=0;Hu?l.indexLabelText="":l.indexLabelTextBlock.maxWidth=0.85*u,0.3*l.indexLabelTextBlock.maxWidthd&&(d=y)),y=y=0,0d&&(d=y)));var K=function(a, +b,c){for(var e=[],d=0;e.push(f[b]),b!==c;b=(b+1+s.length)%s.length);e.sort(function(a,b){return a.y-b.y});for(b=0;bz){n=t.indexLabelTextBlock.x;var k=t.indexLabelTextBlock.y-t.indexLabelTextBlock.height/ +2,w=t.indexLabelTextBlock.y+t.indexLabelTextBlock.height/2,p=h.indexLabelTextBlock.y-h.indexLabelTextBlock.height/2,u=h.indexLabelTextBlock.x+h.indexLabelTextBlock.width,r=h.indexLabelTextBlock.y+h.indexLabelTextBlock.height/2;n=t.indexLabelTextBlock.x+t.indexLabelTextBlock.widthu+q||k>r+q||wa&&(a=l),m!==a&&(b=m,d+=-z),0===l%Math.max(s.length/10,3)&&(g=!0)):g=!0;g&&(0=a.dataSeriesIndexes.length)){var h= +this.data[a.dataSeriesIndexes[0]],s=h.dataPoints,q=10,n=this.plotArea,f=h.dataPointEOs,p=2,r,v=1.3,t=20/180*Math.PI,C=6,x={x:(n.x2+n.x1)/2,y:(n.y2+n.y1)/2},z=0;a=!1;for(var y=0;ya&&(e=a,d=!0);var g=s[b].color?s[b].color:h._colorSet[b%h._colorSet.length];e>c&&ja(k.plotArea.ctx, +f[b].center,f[b].radius,g,h.type,c,e,h.fillOpacity,f[b].percentInnerRadius);if(d)break}l()},function(){k.disableToolTip=!1;k._animator.animate(0,k.animatedRender?500:0,function(a){b(a);l()})})}}};var ra=function(a,d,b,c){"undefined"===typeof b&&(b=1);0>=Math.round(d.y4-d.y1)||(a.save(),a.globalAlpha=b,a.beginPath(),a.moveTo(Math.round(d.x1),Math.round(d.y1)),a.lineTo(Math.round(d.x2),Math.round(d.y2)),a.lineTo(Math.round(d.x3),Math.round(d.y3)),a.lineTo(Math.round(d.x4),Math.round(d.y4)),"undefined"!== +d.x5&&(a.lineTo(Math.round(d.x5),Math.round(d.y5)),a.lineTo(Math.round(d.x6),Math.round(d.y6))),a.closePath(),a.fillStyle=c?c:d.color,a.fill(),a.globalAplha=1,a.restore())};p.prototype.renderFunnel=function(a){function d(){for(var a=0,b=[],c=0;ck?(k=c,m=(b+k)*(d-h)/2,a-=m,n=d-h,h+=d-h,n+=0==k?0:a/k,h+=a/k,m=!0):(n=(Math.abs(ba)*b-Math.sqrt(k))/2,k=b-2*n/Math.abs(ba),h+=n,h>d&&(h-=n, +k=c,m=(b+k)*(d-h)/2,a-=m,n=d-h,h+=d-h,n+=a/k,h+=a/k,m=!0),b=k)),e.push(n);return e}function c(){if(t&&C){for(var a,b,c,e,d,g,l,h,m,n,k,q,s,w,p=[],B=[],x={percent:null,total:null},v=null,y=0;yp[y]&&(p[y]=y!==fa?t.reversed?P[y].x3-P[y].x4:P[y].x2-P[y].x1:P[y].x2-P[y].x1,p[y]/=2));s=b.indexLabelMaxWidth?b.indexLabelMaxWidth:t.options.indexLabelMaxWidth?t.indexLabelMaxWidth:p[y];if(s>p[y]||0>s)s=p[y];B[y]="inside"===t.indexLabelPlacement?P[y].height:!1;x=z.getPercentAndTotal(t,b);if(t.indexLabelFormatter||b.indexLabelFormatter)v={chart:z.options,dataSeries:t,dataPoint:b,total:x.total,percent:x.percent};b=b.indexLabelFormatter?b.indexLabelFormatter(v):b.indexLabel? +z.replaceKeywordsWithValue(b.indexLabel,b,t,y):t.indexLabelFormatter?t.indexLabelFormatter(v):t.indexLabel?z.replaceKeywordsWithValue(t.indexLabel,b,t,y):b.label?b.label:"";0>=n&&(n=0);1E3>s&&1E3-sl?l:t.indexLabelMaxWidth:l,h=J.length-1;0<=h;h--){g=C[J[h].id];c=J[h];e=c.textBlock;b=(a=n(h)b.y&&(d=!0);c=g.indexLabelMaxWidth||l;if(c>l||0>c)c=l;f.push(c)}if(d)for(h=J.length-1;0<=h;h--)a=P[h],J[h].textBlock.maxWidth= +f[f.length-(h+1)],J[h].textBlock.measureText(),J[h].textBlock.x=L-l,c=J[h].textBlock.heightpa+D&&(J[h].textBlock.y=pa+D-J[h].height),J[h].textBlock.ywa+D&&(J[h].textBlock.y=wa+D-J[h].height))}function g(){var a,b,c,e;if("inside"!==t.indexLabelPlacement)for(var d=0;dDa?f(c).x2+1:(a.x2+a.x3)/2+1:(a.x2+a.x3)/2+1:"undefined"!==typeof a.x5?cpa+D&&(J[d].textBlock.y=pa+D-J[d].height),J[d].textBlock.ywa+D&&(J[d].textBlock.y=wa+D-J[d].height)));else for(d=0;d=c?(b=d!=fa?(a.x4+a.x3)/2-e/2:(a.x5+a.x4)/2-e/2,c=d!=fa?(a.y1+a.y3)/2-c/2:(a.y1+a.y4)/2-c/2,J[d].textBlock.x=b, +J[d].textBlock.y=c):J[d].isDirty=!0)}function m(){function a(b,c){var d;if(0>b||b>=J.length)return 0;var e,f=J[b].textBlock;if(0>c){c*=-1;e=q(b);d=l(e,b);if(d>=c)return f.y-=c,c;if(0==b)return 0=c)return f.y+=c,c;if(b==P.length-1)return 0e)&&(l=n(s),!(l>=J.length-1)&&J[s].textBlock.y+J[s].height+ga>J[l].textBlock.y&&(J[s].textBlock.y=J[s].textBlock.y+J[s].height-e>e-J[s].textBlock.y?e+1:e-J[s].height-1))}for(l=P.length-1;0e&&(e=0,J[e].isDirty))break;if(J[l].textBlock.y=f){f=0;h+=J[f].height;break}e=q(f); +if(0>e){f=0;h+=J[f].height;break}}if(f!=l){g=J[f].textBlock.y;a-=g;a=h-a;g=c(a,d,f);break}}}return g}function c(a,b,d){var e=[],f=0,g=0;for(a=Math.abs(a);d<=b;d++)e.push(P[d]);e.sort(function(a,b){return a.height-b.height});for(d=0;d+m.y.toFixed(6))&&(d=g.y+d+ga-m.y,e=a(w,-d),ea?t.reversed?wa-D:pa-D:J[a].textBlock.y+J[a].height+ga)}function k(a,b,c){var d,e,f,l=[],m=D,n=[];-1!==b&&(0<=W.indexOf(b)?(e=W.indexOf(b),W.splice(e,1)):(W.push(b),W=W.sort(function(a,b){return a-b})));if(0===W.length)l= +ia;else{e=D*(1!=W.length||0!=W[0]&&W[0]!=P.length-1?2:1)/h();for(var q=0;qn&&(n*=-1),c.y1+=b-n[d],c.y2+=b-n[d],c.y3+=b-n[d],c.y4+=b-n[d],c.y5&&(c.y5+=b-n[d],c.y6+=b-n[d]),n[d]=b}};a._animator.animate(0,c,function(c){var d=a.plotArea.ctx||a.ctx;ja=!0;d.clearRect(x.x1,x.y1,x.x2-x.x1,x.y2-x.y1);d.fillStyle=a.backgroundColor;d.fillRect(x.x1,x.y1,x.width,x.height);w.changeSection(c,b);var e={};e.dataSeries=t;e.dataPoint=t.reversed?t.dataPoints[C.length-1-b]:t.dataPoints[b];e.index=t.reversed?C.length-1-b:b;a.toolTip.highlightObjects([e]); +for(e=0;ea){b=P[c];break}return b?(a=b.y6?a>b.y6?b.x3+(b.x4-b.x3)/(b.y4-b.y3)*(a-b.y3):b.x2+(b.x3-b.x2)/(b.y3-b.y2)*(a-b.y2):b.x2+(b.x3-b.x2)/(b.y3-b.y2)*(a-b.y2), +{x1:a,x2:a}):-1}function p(a){for(var b=0;b=a.dataSeriesIndexes.length)){for(var t=this.data[a.dataSeriesIndexes[0]],C=t.dataPoints,x=this.plotArea,D=0.025*x.width,y=0.01*x.width,A=0,F=x.height-2*D,E=Math.min(x.width-2*y,2.8*x.height),H=!1,I=0;IF?N=F:0>=N&&(N=0),G>a?G=a-0.5:0>=G&&(G=0)):"pyramid"===t.type&&(G=N=0,t.reversed=t.reversed?!1:!0);var y=I+a/2,$=I,V=I+a,pa=t.reversed?Z:O,K=y-G/2,ea=y+G/2,Da=t.reversed?O+N:Z- +N,wa=t.reversed?O:Z;a=[];var y=[],P=[],E=[],X=O,fa,ba=(Da-pa)/(K-$),ha=-ba,I="area"===(t.valueRepresents?t.valueRepresents:"height")?b():d();if(-1!==I){if(t.reversed)for(E.push(X),G=I.length-1;0a&&(A=a));for(G=0;G\n');c.document.close();setTimeout(function(){c.focus();c.print();setTimeout(function(){b._canvasJSContainer.removeChild(d)},1E3)},500)};p.prototype.getPercentAndTotal=function(a,d){var b=null,c=null, +e=null;if(0<=a.type.indexOf("stacked"))c=0,b=d.x.getTime?d.x.getTime():d.x,b in a.plotUnit.yTotals&&(c=a.plotUnit.yTotals[b],e=isNaN(d.y)?0:100*(d.y/c));else if("pie"===a.type||"doughnut"===a.type||"funnel"===a.type||"pyramid"===a.type){for(b=c=0;b=m||"undefined"=== +typeof m||0>=v||"undefined"===typeof v)){if("horizontal"===this.orientation){n.textBlock=new ka(this.ctx,{x:0,y:0,maxWidth:v,maxHeight:this.itemWrap?m:this.lineHeight,angle:0,text:n.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"});n.textBlock.measureText();null!==this.itemWidth&&(n.textBlock.width=this.itemWidth-(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"=== +n.chartType?2*0.1*this.lineHeight:0)));if(!q||q.width+Math.round(n.textBlock.width+r+l+(0===q.width?0:this.horizontalSpacing)+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))>g)q={items:[],width:0},h.push(q),this.height+=f,f=0;f=Math.max(f,n.textBlock.height)}else n.textBlock=new ka(this.ctx,{x:0,y:0,maxWidth:x,maxHeight:!0===this.itemWrap?m:1.5*this.fontSize,angle:0,text:n.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily, +fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"}),n.textBlock.measureText(),null!==this.itemWidth&&(n.textBlock.width=this.itemWidth-(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))),this.height>0,0),this.dataPoints.length):0):(s=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,s=0>0,0),this.dataPoints.length):0));for(;;){g=0a?c.x/a:a/c.x: +Math.abs(c.x-a);qs-e&&s+e>=this.dataPoints.length)break;-1===m?(e++,m=1):m=-1}return d||b.dataPoint.x!==a?d&&null!==b.dataPoint?b:null:b};F.prototype.getDataPointAtXY=function(a,d,b){if(!this.dataPoints||0===this.dataPoints.length||athis.chart.plotArea.x2||dthis.chart.plotArea.y2)return null;b=b||!1;var c=[],e=0,g=0,m=1,l=!1,k=Infinity, +h=0,s=0,q=0;if("none"!==this.chart.plotInfo.axisPlacement)if(q=(this.chart.axisX[0]?this.chart.axisX[0]:this.chart.axisX2[0]).getXValueAt({x:a,y:d}),this.axisX.logarithmic)var n=Math.log(this.dataPoints[this.dataPoints.length-1].x/this.dataPoints[0].x),q=1>0,0),this.dataPoints.length):0;else n=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,q=0> +0,0),this.dataPoints.length):0;for(;;){g=0=n.x1&&(a<=n.x2&&d>=n.y1&&d<=n.y2)&&(c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1- +a),Math.abs(n.x2-a),Math.abs(n.y1-d),Math.abs(n.y2-d))}),l=!0);break;case "line":case "stepLine":case "spline":case "area":case "stepArea":case "stackedArea":case "stackedArea100":case "splineArea":case "scatter":var u=na("markerSize",f,this)||4,r=b?20:u,p=Math.sqrt(Math.pow(n.x1-a,2)+Math.pow(n.y1-d,2));p<=r&&c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:p});n=Math.abs(n.x1-a);n<=k?k=n:0r&&(p=Math.atan2(d-u.y,a-u.x),0>p&&(p+=2*Math.PI),p=Number(((180*(p/Math.PI)%360+360)%360).toFixed(12)),u=Number(((180*(n.startAngle/Math.PI)%360+360)%360).toFixed(12)),r=Number(((180*(n.endAngle/Math.PI)%360+360)%360).toFixed(12)),0===r&&1=r&&0!==f.y&&(r+=360,pu&&pp.y1&&dp.y6?(g=p.x6+(p.x5-p.x6)/(p.y5-p.y6)*(d-p.y6),p=p.x3+(p.x4-p.x3)/(p.y4-p.y3)*(d-p.y3)):(g=p.x1+(p.x6-p.x1)/(p.y6-p.y1)*(d-p.y1),p=p.x2+(p.x3-p.x2)/(p.y3-p.y2)*(d-p.y2)):(g=p.x1+(p.x4-p.x1)/(p.y4-p.y1)*(d-p.y1),p=p.x2+(p.x3-p.x2)/(p.y3-p.y2)*(d-p.y2)),a>g&&a=n.x1-n.borderThickness/2&&a<=n.x2+n.borderThickness/2&&d>=n.y4-n.borderThickness/2&&d<=n.y1+n.borderThickness/ +2||Math.abs(n.x2-a+n.x1-a)=n.y1&&d<=n.y4)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0;break;case "candlestick":if(a>=n.x1-n.borderThickness/2&&a<=n.x2+n.borderThickness/2&&d>=n.y2-n.borderThickness/2&&d<=n.y3+n.borderThickness/2||Math.abs(n.x2-a+n.x1-a)=n.y1&&d<=n.y4)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a), +Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0;break;case "ohlc":if(Math.abs(n.x2-a+n.x1-a)=n.y2&&d<=n.y3||a>=n.x1&&a<=(n.x2+n.x1)/2&&d>=n.y1-n.borderThickness/2&&d<=n.y1+n.borderThickness/2||a>=(n.x1+n.x2)/2&&a<=n.x2&&d>=n.y4-n.borderThickness/2&&d<=n.y4+n.borderThickness/2)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0}if(l||1E3q-e&&q+e>= +this.dataPoints.length)break;-1===m?(e++,m=1):m=-1}a=null;for(d=0;dq[f].endValue;f++);a=f=q[f].startValue&&b<=q[f].endValue;p=b;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:p,label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:ba(p,this.valueFormatString,this.chart._cultureInfo),a=new ka(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:m,angle:this.labelAngle,text:this.prefix+a+this.suffix,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius, +horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle",borderThickness:0}),this._labels.push({position:p,textBlock:a,effectiveHeight:null}))}f=n;for(b=this.intervalStartPosition;b<=e;b=parseFloat(1E-12>this.interval?this.logarithmic&&this.equidistantInterval?b*Math.pow(this.logarithmBase,this.interval):b+this.interval:(this.logarithmic&&this.equidistantInterval? +b*Math.pow(this.logarithmBase,this.interval):b+this.interval).toFixed(12))){for(;fq[f].endValue;f++);a=f=q[f].startValue&&b<=q[f].endValue;p=b;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:p,label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:ba(p,this.valueFormatString,this.chart._cultureInfo),a=new ka(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:m,angle:this.labelAngle,text:this.prefix+a+this.suffix, +horizontalAlign:"left",backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"}),this._labels.push({position:p,textBlock:a,effectiveHeight:null}))}}else for(this.intervalStartPosition=this.getLabelStartPoint(new Date(this.viewportMinimum), +this.intervalType,this.interval),e=Ya(new Date(this.viewportMaximum),this.interval,this.intervalType),f=n,b=this.intervalStartPosition;bq[f].endValue;f++);p=a;a=f=q[f].startValue&&a<=q[f].endValue;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:new Date(p),label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:Ca(p,this.valueFormatString,this.chart._cultureInfo), +a=new ka(this.ctx,{x:0,y:0,maxWidth:g,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,maxHeight:m,angle:this.labelAngle,text:this.prefix+a+this.suffix,horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"}),this._labels.push({position:p,textBlock:a,effectiveHeight:null, +breaksLabelType:void 0}))}if("bottom"===this._position||"top"===this._position)l=this.logarithmic&&!this.equidistantInterval&&2<=this._labels.length?this.lineCoordinates.width*Math.log(Math.min(this._labels[this._labels.length-1].position/this._labels[this._labels.length-2].position,this._labels[1].position/this._labels[0].position))/Math.log(this.range):this.lineCoordinates.width/(this.logarithmic&&this.equidistantInterval?Math.log(this.range)/Math.log(this.logarithmBase):Math.abs(this.range))*S[this.intervalType+ +"Duration"]*this.interval,g="undefined"===typeof this.options.labelMaxWidth?0.5*this.chart.width>>0:this.options.labelMaxWidth,this.chart.panEnabled||(m="undefined"===typeof this.options.labelWrap||this.labelWrap?0.8*this.chart.height>>0:1.5*this.labelFontSize);else if("left"===this._position||"right"===this._position)l=this.logarithmic&&!this.equidistantInterval&&2<=this._labels.length?this.lineCoordinates.height*Math.log(Math.min(this._labels[this._labels.length-1].position/this._labels[this._labels.length- +2].position,this._labels[1].position/this._labels[0].position))/Math.log(this.range):this.lineCoordinates.height/(this.logarithmic&&this.equidistantInterval?Math.log(this.range)/Math.log(this.logarithmBase):Math.abs(this.range))*S[this.intervalType+"Duration"]*this.interval,this.chart.panEnabled||(g="undefined"===typeof this.options.labelMaxWidth?0.3*this.chart.width>>0:this.options.labelMaxWidth),m="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize; +for(c=0;cthis.labelAngle?this.labelAngle-=180:270<=this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360)),"bottom"===this._position||"top"===this._position)if(g=0.9*l>>0,n=0,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize= +this.labelFontSize;this.sessionVariables.labelMaxWidth=g;this.sessionVariables.labelMaxHeight=m;this.sessionVariables.labelAngle=this.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;bn&&(v=b,n=p.width)}b=0;for(b=this.intervalStartPosition< +this.viewportMinimum?1:0;b>0>2*g&&(this.sessionVariables.labelAngle=-25)):(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle):u(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelMaxWidth= +g,B.width+d.width>>0>2*g&&(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p)):(this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelWrap=this.labelWrap);else{if(u(this.options.labelWrap))if(!u(this.options.labelMaxWidth))this.options.labelMaxWidth>0,f=this.labelFontSize,nq&&(q=c-2*g,c>=2*g&&c<2.2*g?(this.sessionVariables.labelMaxWidth=g,u(this.options.labelFontSize)&&12=2.2*g&&c<2.8*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelFontSize=f):c>=2.8*g&&c<3.2*g?(this.sessionVariables.labelMaxWidth=Math.max(g,n),this.sessionVariables.labelWrap=!0,u(this.options.labelFontSize)&&12=3.2*g&&c<3.6*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelFontSize=this.labelFontSize):c>3.6*g&&c<5*g?(u(this.options.labelFontSize)&&125*g&&(this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelFontSize=f,this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelAngle=this.labelAngle));else if(v===b&&(0===v&&n+this._labels[v+1].textBlock.measureText().width-2*g>q||v===this._labels.length-1&&n+this._labels[v-1].textBlock.measureText().width-2*g>q||0q&&n+this._labels[v-1].textBlock.measureText().width- +2*g>q))q=0===v?n+this._labels[v+1].textBlock.measureText().width-2*g:n+this._labels[v-1].textBlock.measureText().width-2*g,this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p;else if(0===q)for(this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,c=0;c>0>2*g&&(this.sessionVariables.labelAngle=-25))}else(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxHeight=0===this.labelAngle?m:Math.min((c-g*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/ +Math.sin(Math.PI/180*Math.abs(this.labelAngle)),c),p=0!=this.labelAngle?(h-(k+a.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/Math.sin(Math.PI/180*Math.abs(this.labelAngle)):g,this.sessionVariables.labelMaxHeight=m=this.labelWrap?(h-p*Math.sin(Math.PI/180*Math.abs(this.labelAngle)))/Math.cos(Math.PI/180*Math.abs(this.labelAngle)):1.5*this.labelFontSize,u(this.options.labelWrap))?u(this.options.labelWrap)&&(this.labelWrap&&!u(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap= +this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p,this.sessionVariables.labelMaxHeight=m):(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelMaxHeight=c<0.9*l?0.9*l:c,this.sessionVariables.labelWrap=this.labelWrap)):(this.options.labelWrap?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p): +(u(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p,this.sessionVariables.labelWrap=this.labelWrap),this.sessionVariables.labelMaxHeight=m)}for(c=0;c>0:this.options.labelMaxWidth,m="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize=this.labelFontSize;this.sessionVariables.labelMaxWidth= +g;this.sessionVariables.labelMaxHeight=m;this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;b>0,l-2*m>n&&(n=l-2*m,l>=2*m&&l<2.4*m?(u(this.options.labelFontSize)&&12=2.4*m&&l<2.8*m?(this.sessionVariables.labelMaxHeight=c,this.sessionVariables.labelFontSize=this.labelFontSize,this.sessionVariables.labelWrap=!0):l>=2.8*m&&l<3.2*m?(this.sessionVariables.labelMaxHeight=m,this.sessionVariables.labelWrap=!0,u(this.options.labelFontSize)&&12< +this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle):l>=3.2*m&&l<3.6*m?(this.sessionVariables.labelMaxHeight=c,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelFontSize=this.labelFontSize):l>3.6*m&&l<10*m?(u(this.options.labelFontSize)&& +1210*m&&l<50*m&&(u(this.options.labelFontSize)&&12this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=c):(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:g,this.sessionVariables.labelMaxHeight=0===this.labelAngle?m:c,u(this.options.labelMaxWidth)&& +(this.sessionVariables.labelAngle=this.labelAngle))):this.options.labelWrap?(this.sessionVariables.labelMaxHeight=0===this.labelAngle?m:c,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=g):(this.sessionVariables.labelMaxHeight=m,u(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=this.labelWrap)}for(c=0;c>0:1.5*this.labelFontSize;if("left"===this._position||"right"===this._position)z=u(g.options.labelWrap)?this.sessionVariables.labelMaxHeight:g.labelWrap?0.8*this.chart.width>>0:1.5*this.labelFontSize;u(g.labelBackgroundColor)&&(g.labelBackgroundColor="#EEEEEE")}else m="bottom"===this._position||"top"===this._position?0.9*this.chart.width>>0:0.9*this.chart.height>> +0,z=u(g.options.labelWrap)||g.labelWrap?"bottom"===this._position||"top"===this._position?0.8*this.chart.width>>0:0.8*this.chart.height>>0:1.5*this.labelFontSize,u(g.labelBackgroundColor)&&(u(g.startValue)&&0!==g.startValue?g.labelBackgroundColor=r?"transparent":null:g.labelBackgroundColor="#EEEEEE");a=new ka(this.ctx,{x:0,y:0,backgroundColor:g.labelBackgroundColor,borderColor:g.labelBorderColor,borderThickness:g.labelBorderThickness,cornerRadius:g.labelCornerRadius,maxWidth:g.options.labelMaxWidth? +g.options.labelMaxWidth:m,maxHeight:z,angle:this.labelAngle,text:g.labelFormatter?g.labelFormatter({chart:this.chart,axis:this,stripLine:g}):g.label,horizontalAlign:"left",fontSize:"outside"===g.labelPlacement?g.options.labelFontSize?g.labelFontSize:this.labelFontSize:g.labelFontSize,fontFamily:"outside"===g.labelPlacement?g.options.labelFontFamily?g.labelFontFamily:this.labelFontFamily:g.labelFontFamily,fontWeight:"outside"===g.labelPlacement?g.options.labelFontWeight?g.labelFontWeight:this.labelFontWeight: +g.labelFontWeight,fontColor:g.labelFontColor||g.color,fontStyle:"outside"===g.labelPlacement?g.options.labelFontStyle?g.labelFontStyle:this.fontWeight:g.labelFontStyle,textBaseline:"middle"});this._stripLineLabels.push({position:g.value,textBlock:a,effectiveHeight:null,stripLine:g})}};z.prototype.createLabelsAndCalculateWidth=function(){var a=0,d=0;this._labels=[];this._stripLineLabels=[];var b=this.chart.isNavigator?0:5;if("left"===this._position||"right"===this._position){this.createLabels();for(d= +0;d=this.viewportMinimum&&this._stripLineLabels[d].stripLine.value<=this.viewportMaximum)&& +(c=this._stripLineLabels[d].textBlock,e=c.measureText(),g=0===this.labelAngle?e.width:e.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+(e.height-c.fontSize/2)*Math.sin(Math.PI/180*Math.abs(this.labelAngle)),a=this.viewportMinimum&&this._stripLineLabels[b].stripLine.value<=this.viewportMaximum)&&(d=this._stripLineLabels[b].textBlock,e=d.measureText(),g=0===this.labelAngle?e.height:e.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(e.height-d.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)),an[f].viewportMaximum);v++)r[v].endValue=n[f].viewPortMinimum&&(n[f].scaleBreaks.lastBreakIndex=v));for(var z=v=0,t=0,C=0,x=0,D=0,y=0,A,E,F=l=0,H,I,L,r=H=I=L=!1,f=0;fv;){var G=0,R=0,S=0,U=0,W=e=0,K=0,$=0,V=0,X=0,P=0,ba=0;if(b&& +0p.width- +q?p.width-q:g.x2-ba-$);if(a&&0p.width-q?p.width-q:g.x2-ba-$),a[f]._labels&&1k&&(l+=0a[f].labelAngle?A-zk&&(l=E+t/2-k-ba),A-za[f].labelAngle&&0p.width-q?p.width-q:g.x2-ba-$),d[f].lineCoordinates.width=Math.abs(k-m),d[f]._labels&&1v;){V=U=R=S=$=K=W=e=Q=O=G=X=0;if(a&&0p.width-10?p.width-10:g.x2-V-W),b[f].labelAutoFit&&!u(C)&&(0b[f].labelAngle?Math.max(m,C):0===b[f].labelAngle? +Math.max(m,C/2):m),0c[f].chart.width-10?c[f].chart.width-10:g.x2-V-W),c[f]&& +c[f].labelAutoFit&&!u(D)&&(0b[f].chart.height-10?b[f].chart.height-10:g.y2),b[f].lineCoordinates.y1=l-(q[f]+b[f].margin+ +X),b[f].lineCoordinates.y2=l-(q[f]+b[f].margin+X),b[f].bounds={x1:m,y1:l-(q[f]+X+b[f].margin),x2:k,y2:h-(X+b[f].margin),width:k-m,height:q[f]},b[f].title&&(b[f]._titleTextBlock.maxWidth=0p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2-S):g.y2>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2;if(b&&0b[K].labelAngle?Math.max(k,C):0===b[K].labelAngle?Math.max(k,C/2):k,m=0>b[K].labelAngle||0===b[K].labelAngle?k-U:m);if(c&&0p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2-S):g.y2>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2;if(b&&0b[K].labelAngle?Math.max(k,C):0===b[K].labelAngle?Math.max(k,C/2):k,m=0>b[K].labelAngle||0=== +b[K].labelAngle?k-V:m);if(c&&0d[g].spacing?0:Math.abs(d[g].spacing/b),this.logarithmic&&(d[g].size=Math.pow(this.logarithmBase,d[g].size))};z.prototype.calculateBreaksInPixels=function(){if(!(this.scaleBreaks&&0>=this.scaleBreaks._appliedBreaks.length)){var a=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];a.length&&(this.scaleBreaks.firstBreakIndex=this.scaleBreaks.lastBreakIndex=null);for(var d=0;dthis.conversionParameters.maximum);d++)a[d].endValue< +this.conversionParameters.minimum||(u(this.scaleBreaks.firstBreakIndex)&&(this.scaleBreaks.firstBreakIndex=d),a[d].startValue>=this.conversionParameters.minimum&&(a[d].startPixel=this.convertValueToPixel(a[d].startValue),this.scaleBreaks.lastBreakIndex=d),a[d].endValue<=this.conversionParameters.maximum&&(a[d].endPixel=this.convertValueToPixel(a[d].endValue)))}};z.prototype.renderLabelsTicksAndTitle=function(){var a=this,d=!1,b=0,c=0,e=1,g=0;0!==this.labelAngle&&360!==this.labelAngle&&(e=1.2);if("undefined"=== +typeof this.options.interval){if("bottom"===this._position||"top"===this._position)if(this.logarithmic&&!this.equidistantInterval&&this.labelAutoFit){for(var b=[],e=0!==this.labelAngle&&360!==this.labelAngle?1:1.2,m,l=this.viewportMaximum,k=this.lineCoordinates.width/Math.log(this.range),h=this._labels.length-1;0<=h;h--){q=this._labels[h];if(q.positionthis.viewportMaximum||!(h===this._labels.length-1||mthis.lineCoordinates.width*e&&this.labelAutoFit&&(d=!0)}if("left"===this._position||"right"===this._position)if(this.logarithmic&& +!this.equidistantInterval&&this.labelAutoFit){for(var b=[],p,l=this.viewportMaximum,k=this.lineCoordinates.height/Math.log(this.range),h=this._labels.length-1;0<=h;h--){q=this._labels[h];if(q.positionthis.viewportMaximum||!(h===this._labels.length-1||pthis.lineCoordinates.height*e&&this.labelAutoFit&&(d=!0)}}this.logarithmic&&(!this.equidistantInterval&&this.labelAutoFit)&&this._labels.sort(function(a,b){return a.position-b.position});var h=0,q,n;if("bottom"===this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,n.y<<0),this.ctx.lineTo(c,n.y+this.tickLength<<0),this.ctx.stroke()),0===q.textBlock.angle?(n.x-=q.textBlock.width/2,n.y="inside"===this.labelPlacement? +n.y-(this.tickLength+q.textBlock.fontSize/2):n.y+this.tickLength+q.textBlock.fontSize/2):(n.x="inside"===this.labelPlacement?0>this.labelAngle?n.x:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):n.x-(0>this.labelAngle?q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0),n.y="inside"===this.labelPlacement?0>this.labelAngle?n.y-this.tickLength-5:n.y-this.tickLength-Math.abs(q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle)+5):n.y+this.tickLength+Math.abs(0>this.labelAngle?q.textBlock.width* +Math.sin(Math.PI/180*this.labelAngle)-5:5)),q.textBlock.x=n.x,q.textBlock.y=n.y);"inside"===this.labelPlacement&&this.chart.addEventListener("dataAnimationIterationEnd",function(){for(h=0;ha.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0;a.ctx.save(); +a.ctx.beginPath();a.ctx.moveTo(b,n.y<<0);a.ctx.lineTo(b,n.y-a.tickLength<<0);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.bounds.y2-this._titleTextBlock.height-3,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("top"===this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,n.y<<0),this.ctx.lineTo(c,n.y-this.tickLength<<0),this.ctx.stroke()),0===q.textBlock.angle?(n.x-=q.textBlock.width/2,n.y="inside"===this.labelPlacement? +n.y+this.labelFontSize/2+this.tickLength+5:n.y-(this.tickLength+q.textBlock.height-q.textBlock.fontSize/2)):(n.x="inside"===this.labelPlacement?0a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&& +(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(b,n.y<<0);a.ctx.lineTo(b,n.y+a.tickLength<<0);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.bounds.y1+1,this.titleMaxWidth= +this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("left"===this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0,this.ctx.beginPath(),this.ctx.moveTo(n.x<< +0,c),this.ctx.lineTo(n.x-this.tickLength<<0,c),this.ctx.stroke()),0===this.labelAngle?(q.textBlock.y=n.y,q.textBlock.x="inside"===this.labelPlacement?n.x+this.tickLength+5:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5):(q.textBlock.y="inside"===this.labelPlacement?n.y:n.y-q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle),q.textBlock.x="inside"===this.labelPlacement?n.x+this.tickLength+5:0a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness; +a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(n.x<<0,b);a.ctx.lineTo(n.x+a.tickLength<<0,b);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.bounds.x1+1,this._titleTextBlock.y=this.lineCoordinates.height/2+this._titleTextBlock.width/2+this.lineCoordinates.y1,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("right"=== +this._position){for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0,this.ctx.beginPath(),this.ctx.moveTo(n.x<<0,c),this.ctx.lineTo(n.x+this.tickLength<<0,c),this.ctx.stroke()),0===this.labelAngle? +(q.textBlock.y=n.y,q.textBlock.x="inside"===this.labelPlacement?n.x-q.textBlock.width-this.tickLength-5:n.x+this.tickLength+5):(q.textBlock.y="inside"===this.labelPlacement?n.y-q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle):0>this.labelAngle?n.y:n.y-(q.textBlock.height-q.textBlock.fontSize/2-5)*Math.cos(Math.PI/180*this.labelAngle),q.textBlock.x="inside"===this.labelPlacement?n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5:0a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.y<< +0)+0.5:n.y<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(n.x<<0,b);a.ctx.lineTo(n.x-a.tickLength<<0,b);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.bounds.x2-1,this._titleTextBlock.y=this.lineCoordinates.height/2-this._titleTextBlock.width/2+this.lineCoordinates.y1,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}g=0;if("inside"===this.labelPlacement)this.chart.addEventListener("dataAnimationIterationEnd", +function(){for(h=0;ha.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)||(a.ctx.save(),a.ctx.beginPath(),q.textBlock.render(!0),a.ctx.restore())},this);else for(h=0;hthis.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||q.textBlock.render(!0)};z.prototype.renderInterlacedColors=function(){var a=this.chart.plotArea.ctx,d,b,c=this.chart.plotArea, +e=0;d=!0;if(("bottom"===this._position||"top"===this._position)&&this.interlacedColor)for(a.fillStyle=this.interlacedColor,e=0;ethis._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(Math.min(b.x,d.x),c.y1,Math.abs(b.x-d.x),Math.abs(c.y1-c.y2)),d=!1):d=!0;else if(("left"===this._position||"right"===this._position)&&this.interlacedColor)for(a.fillStyle= +this.interlacedColor,e=0;ethis._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(c.x1,Math.min(b.y,d.y),Math.abs(c.x1-c.x2),Math.abs(d.y-b.y)),d=!1):d=!0;a.beginPath()};z.prototype.renderStripLinesOfThicknessType=function(a){if(this.stripLines&&0this.viewportMaximum||u(h.value)||isNaN(this.range))||l.push(h))}for(c=0;cthis.viewportMaximum||isNaN(this.range))){a=this.getPixelCoordinatesOnAxis(b.position);if("outside"===b.stripLine.labelPlacement)if(h&&(this.ctx.strokeStyle= +h.color,"pixel"===h._thicknessType&&(this.ctx.lineWidth=h.thickness)),"bottom"===this._position){var p=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0;this.ctx.beginPath();this.ctx.moveTo(p,a.y<<0);this.ctx.lineTo(p,a.y+this.tickLength<<0);this.ctx.stroke();0===this.labelAngle?(a.x-=b.textBlock.width/2,a.y+=this.tickLength+b.textBlock.fontSize/2):(a.x-=0>this.labelAngle?b.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0,a.y+=this.tickLength+Math.abs(0>this.labelAngle?b.textBlock.width*Math.sin(Math.PI/ +180*this.labelAngle)-5:5))}else"top"===this._position?(p=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0,this.ctx.beginPath(),this.ctx.moveTo(p,a.y<<0),this.ctx.lineTo(p,a.y-this.tickLength<<0),this.ctx.stroke(),0===this.labelAngle?(a.x-=b.textBlock.width/2,a.y-=this.tickLength+b.textBlock.height):(a.x+=(b.textBlock.height-this.tickLength-this.labelFontSize/2)*Math.sin(Math.PI/180*this.labelAngle)-(0this.labelAngle?a.y:a.y-(b.textBlock.height-b.textBlock.fontSize/ +2-5)*Math.cos(Math.PI/180*this.labelAngle),a.x=0this.chart.plotArea.x1?u(h.startValue)?a.x-=b.textBlock.height-b.textBlock.fontSize/ +2:a.x-=b.textBlock.height/2-b.textBlock.fontSize/2+3:(b.textBlock.angle=90,u(h.startValue)?a.x+=b.textBlock.height-b.textBlock.fontSize/2:a.x+=b.textBlock.height/2-b.textBlock.fontSize/2+3),a.y=-90===b.textBlock.angle?"near"===b.stripLine.labelAlign?this.chart.plotArea.y2-3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1+b.textBlock.width)/2:this.chart.plotArea.y1+b.textBlock.width+3:"near"===b.stripLine.labelAlign?this.chart.plotArea.y2-b.textBlock.width-3:"center"=== +b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1-b.textBlock.width)/2:this.chart.plotArea.y1+3):"top"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.height-3,b.textBlock.measureText(),a.x-b.textBlock.height>this.chart.plotArea.x1?u(h.startValue)?a.x-=b.textBlock.height-b.textBlock.fontSize/2:a.x-=b.textBlock.height/2-b.textBlock.fontSize/2+3:(b.textBlock.angle=90,u(h.startValue)?a.x+= +b.textBlock.height-b.textBlock.fontSize/2:a.x+=b.textBlock.height/2-b.textBlock.fontSize/2+3),a.y=-90===b.textBlock.angle?"near"===b.stripLine.labelAlign?this.chart.plotArea.y1+b.textBlock.width+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1+b.textBlock.width)/2:this.chart.plotArea.y2-3:"near"===b.stripLine.labelAlign?this.chart.plotArea.y1+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1-b.textBlock.width)/2:this.chart.plotArea.y2- +b.textBlock.width-3):"left"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.width-3,b.textBlock.angle=0,b.textBlock.measureText(),a.y-b.textBlock.height>this.chart.plotArea.y1?u(h.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize+3:a.y-b.textBlock.heightthis.chart.plotArea.y1? +u(h.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize/2-3:a.y-b.textBlock.heightthis.viewportMaximum|| +isNaN(this.range))||a[d].render(this.maskCtx);this.maskCtx.restore()}};z.prototype.renderCrosshair=function(a,d){this.crosshair.render(a,d)};z.prototype.renderGrid=function(){if(this.gridThickness&&0this.viewportMaximum||this._labels[c].breaksLabelType)||(a.beginPath(),d=this.getPixelCoordinatesOnAxis(this._labels[c].position),d=1===a.lineWidth%2?(d.x<<0)+0.5:d.x<<0,a.moveTo(d,b.y1<<0),a.lineTo(d,b.y2<<0),a.stroke());else if("left"===this._position||"right"===this._position)for(var c=0;cthis.viewportMaximum||this._labels[c].breaksLabelType)||(a.beginPath(), +d=this.getPixelCoordinatesOnAxis(this._labels[c].position),d=1===a.lineWidth%2?(d.y<<0)+0.5:d.y<<0,a.moveTo(b.x1<<0,d),a.lineTo(b.x2<<0,d),a.stroke());a.restore()}};z.prototype.renderAxisLine=function(){var a=this.chart.ctx,d=r?this.chart._preRenderCtx:a,b=Math.ceil(this.tickThickness/(this.reversed?-2:2)),c=Math.ceil(this.tickThickness/(this.reversed?2:-2)),e,g;d.save();if("bottom"===this._position||"top"===this._position){if(this.lineThickness){this.reversed?(e=this.lineCoordinates.x2,g=this.lineCoordinates.x1): +(e=this.lineCoordinates.x1,g=this.lineCoordinates.x2);d.lineWidth=this.lineThickness;d.strokeStyle=this.lineColor?this.lineColor:"black";d.setLineDash&&d.setLineDash(R(this.lineDashType,this.lineThickness));var m=1===this.lineThickness%2?(this.lineCoordinates.y1<<0)+0.5:this.lineCoordinates.y1<<0;d.beginPath();if(this.scaleBreaks&&!u(this.scaleBreaks.firstBreakIndex))if(u(this.scaleBreaks.lastBreakIndex))e=this.scaleBreaks._appliedBreaks[this.scaleBreaks.firstBreakIndex].endPixel+c;else for(var l= +this.scaleBreaks.firstBreakIndex;l<=this.scaleBreaks.lastBreakIndex;l++)d.moveTo(e,m),d.lineTo(this.scaleBreaks._appliedBreaks[l].startPixel+b,m),e=this.scaleBreaks._appliedBreaks[l].endPixel+c;e&&(d.moveTo(e,m),d.lineTo(g,m));d.stroke()}}else if(("left"===this._position||"right"===this._position)&&this.lineThickness){this.reversed?(e=this.lineCoordinates.y1,g=this.lineCoordinates.y2):(e=this.lineCoordinates.y2,g=this.lineCoordinates.y1);d.lineWidth=this.lineThickness;d.strokeStyle=this.lineColor; +d.setLineDash&&d.setLineDash(R(this.lineDashType,this.lineThickness));m=1===this.lineThickness%2?(this.lineCoordinates.x1<<0)+0.5:this.lineCoordinates.x1<<0;d.beginPath();if(this.scaleBreaks&&!u(this.scaleBreaks.firstBreakIndex))if(u(this.scaleBreaks.lastBreakIndex))e=this.scaleBreaks._appliedBreaks[this.scaleBreaks.firstBreakIndex].endPixel+b;else for(l=this.scaleBreaks.firstBreakIndex;l<=this.scaleBreaks.lastBreakIndex;l++)d.moveTo(m,e),d.lineTo(m,this.scaleBreaks._appliedBreaks[l].startPixel+c), +e=this.scaleBreaks._appliedBreaks[l].endPixel+b;e&&(d.moveTo(m,e),d.lineTo(m,g));d.stroke()}r&&(a.drawImage(this.chart._preRenderCanvas,0,0,this.chart.width,this.chart.height),this.chart._breaksCanvasCtx&&this.chart._breaksCanvasCtx.drawImage(this.chart._preRenderCanvas,0,0,this.chart.width,this.chart.height),d.clearRect(0,0,this.chart.width,this.chart.height));d.restore()};z.prototype.getPixelCoordinatesOnAxis=function(a){var d={};if("bottom"===this._position||"top"===this._position)d.x=this.convertValueToPixel(a), +d.y=this.lineCoordinates.y1;if("left"===this._position||"right"===this._position)d.y=this.convertValueToPixel(a),d.x=this.lineCoordinates.x2;return d};z.prototype.convertPixelToValue=function(a){if("undefined"===typeof a)return null;var d=0,b=0,c,d=!0,e=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[],b="number"===typeof a?a:"left"===this._position||"right"===this._position?a.y:a.x;if(this.logarithmic){a=c=Math.pow(this.logarithmBase,(b-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit); +if(b<=this.conversionParameters.reference===("left"===this._position||"right"===this._position)!==this.reversed)for(b=0;be[b].startValue/this.conversionParameters.minimum){c/=e[b].startValue/this.conversionParameters.minimum;if(ce[b].startValue/e[b-1].endValue){c/=e[b].startValue/e[b-1].endValue;if(cthis.conversionParameters.minimum))if(d)if(e[b].endValue>this.conversionParameters.minimum){if(1e[b].startValue){a=Math.pow(e[b].endValue/e[b].startValue,Math.log(c)/Math.log(e[b].size));break}else a*=e[b].startValue/this.conversionParameters.minimum*Math.pow(e[b].size,Math.log(e[b].startValue/this.conversionParameters.minimum)/Math.log(e[b].endValue/e[b].startValue))*c,c*=Math.pow(e[b].size,Math.log(this.conversionParameters.minimum/e[b].startValue)/Math.log(e[b].endValue/e[b].startValue));d=!1}else if(c1/e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1>=e[b].size?1:Math.log(c)/Math.log(e[b].size))*c;break}else a/=e[b].endValue/e[b].startValue/e[b].size;c*=e[b].size;d=!1}else break;else if(c1/e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1>=e[b].size?1:Math.log(c)/Math.log(e[b].size))*c;break}else a/=e[b].endValue/e[b].startValue/e[b].size;c*=e[b].size}else break; +d=a*this.viewportMinimum}else{a=c=(b-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit;if(b<=this.conversionParameters.reference===("left"===this._position||"right"===this._position)!==this.reversed)for(b=0;b=e[b].size?0:c*(e[b].endValue- +e[b].startValue)/e[b].size;break}else a+=e[b].endValue-this.conversionParameters.minimum-e[b].size*(e[b].endValue-this.conversionParameters.minimum)/(e[b].endValue-e[b].startValue),c-=e[b].size*(e[b].endValue-this.conversionParameters.minimum)/(e[b].endValue-e[b].startValue);d=!1}else if(c>e[b].startValue-this.conversionParameters.minimum){c-=e[b].startValue-this.conversionParameters.minimum;if(ce[b].startValue-e[b-1].endValue){c-=e[b].startValue-e[b-1].endValue;if(cthis.conversionParameters.minimum))if(d)if(e[b].endValue>this.conversionParameters.minimum)if(e[b].size&&this.conversionParameters.minimum+c*(e[b].endValue- +e[b].startValue)/e[b].size>e[b].startValue){a=0>=e[b].size?0:c*(e[b].endValue-e[b].startValue)/e[b].size;break}else a+=e[b].startValue-this.conversionParameters.minimum+e[b].size*(this.conversionParameters.minimum-e[b].startValue)/(e[b].endValue-e[b].startValue),c+=e[b].size*(this.conversionParameters.minimum-e[b].startValue)/(e[b].endValue-e[b].startValue),d=!1;else if(c-1*e[b].size){a+=(e[b].endValue- +e[b].startValue)*(0===e[b].size?1:c/e[b].size)+c;break}else a-=e[b].endValue-e[b].startValue-e[b].size;c+=e[b].size;d=!1}else break;else if(c-1*e[b].size){a+=(e[b].endValue-e[b].startValue)*(0===e[b].size?1:c/e[b].size)+c;break}else a-=e[b].endValue-e[b].startValue-e[b].size;c+=e[b].size}else break;d=this.conversionParameters.minimum+a}return d};z.prototype.convertValueToPixel=function(a){a=this.getApparentDifference(this.conversionParameters.minimum, +a,a);return this.logarithmic?this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*Math.log(a/this.conversionParameters.minimum)/this.conversionParameters.lnLogarithmBase+0.5<<0:"axisX"===this.type?this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5<<0:this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5};z.prototype.getApparentDifference=function(a, +d,b,c){var e=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];if(this.logarithmic){b=u(b)?d/a:b;for(var g=0;ge[g].endValue||(a<=e[g].startValue&&d>=e[g].endValue?b=b/e[g].endValue*e[g].startValue*e[g].size:a>=e[g].startValue&&d>=e[g].endValue?b=b/e[g].endValue*a*Math.pow(e[g].size,Math.log(e[g].endValue/a)/Math.log(e[g].endValue/e[g].startValue)):a<=e[g].startValue&&d<=e[g].endValue?b=b/d*e[g].startValue*Math.pow(e[g].size,Math.log(d/e[g].startValue)/Math.log(e[g].endValue/ +e[g].startValue)):!c&&(a>e[g].startValue&&de[g].endValue||(a<=e[g].startValue&&d>=e[g].endValue?b=b-e[g].endValue+e[g].startValue+e[g].size:a>e[g].startValue&&d>=e[g].endValue?b=b-e[g].endValue+a+e[g].size*(e[g].endValue-a)/(e[g].endValue-e[g].startValue):a<=e[g].startValue&&de[g].startValue&&da[e].endValue||(this.viewportMinimum>=a[e].startValue&&this.viewportMaximum<= +a[e].endValue?b=0:this.viewportMinimum<=a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c/a[e].endValue*a[e].startValue,b=0a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c/a[e].endValue*this.viewportMinimum,b=0a[e].endValue||(this.viewportMinimum>=a[e].startValue&&this.viewportMaximum<=a[e].endValue?b=0:this.viewportMinimum<=a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c-a[e].endValue+a[e].startValue,b=0a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c-a[e].endValue+this.viewportMinimum, +b=0this.maxWidth?8:6);var a=Math.max(c,Math.floor(this.maxWidth/a)),e,g,m,c=0;!u(this.options.viewportMinimum)&&(!u(this.options.viewportMaximum)&&this.options.viewportMinimum>=this.options.viewportMaximum)&& +(this.viewportMinimum=this.viewportMaximum=null);if(u(this.options.viewportMinimum)&&!u(this.sessionVariables.newViewportMinimum)&&!isNaN(this.sessionVariables.newViewportMinimum))this.viewportMinimum=this.sessionVariables.newViewportMinimum;else if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=this.minimum;if(u(this.options.viewportMaximum)&&!u(this.sessionVariables.newViewportMaximum)&&!isNaN(this.sessionVariables.newViewportMaximum))this.viewportMaximum=this.sessionVariables.newViewportMaximum; +else if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;if(this.scaleBreaks)for(c=0;c=this.scaleBreaks._appliedBreaks[c].startValue||!u(this.options.minimum)&&this.options.minimum>=this.scaleBreaks._appliedBreaks[c].startValue||!u(this.options.viewportMinimum)&&this.viewportMinimum>=this.scaleBreaks._appliedBreaks[c].startValue)&& +(!u(this.sessionVariables.newViewportMaximum)&&this.sessionVariables.newViewportMaximum<=this.scaleBreaks._appliedBreaks[c].endValue||!u(this.options.maximum)&&this.options.maximum<=this.scaleBreaks._appliedBreaks[c].endValue||!u(this.options.viewportMaximum)&&this.viewportMaximum<=this.scaleBreaks._appliedBreaks[c].endValue)){this.scaleBreaks._appliedBreaks.splice(c,1);break}if("axisX"===this.type){if(this.dataSeries&&0g?(c=Math.min(0.01*Math.abs(this.getApparentDifference(g,e,null,!0)),5),0<=g?e=g-c:g=isFinite(e)?e+c:0):(c=Math.min(0.01*Math.abs(this.getApparentDifference(e,g, +null,!0)),0.05),0!==g&&(g+=c),0!==e&&(e-=c)),m=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1g&&(g=0));c=this.getApparentDifference(isNaN(this.viewportMinimum)||null===this.viewportMinimum?e:this.viewportMinimum,isNaN(this.viewportMaximum)||null===this.viewportMaximum?g:this.viewportMaximum,null, +!0);if("axisX"===this.type&&b){this.intervalType||(c/1<=a?(this.interval=1,this.intervalType="millisecond"):c/2<=a?(this.interval=2,this.intervalType="millisecond"):c/5<=a?(this.interval=5,this.intervalType="millisecond"):c/10<=a?(this.interval=10,this.intervalType="millisecond"):c/20<=a?(this.interval=20,this.intervalType="millisecond"):c/50<=a?(this.interval=50,this.intervalType="millisecond"):c/100<=a?(this.interval=100,this.intervalType="millisecond"):c/200<=a?(this.interval=200,this.intervalType= +"millisecond"):c/250<=a?(this.interval=250,this.intervalType="millisecond"):c/300<=a?(this.interval=300,this.intervalType="millisecond"):c/400<=a?(this.interval=400,this.intervalType="millisecond"):c/500<=a?(this.interval=500,this.intervalType="millisecond"):c/(1*S.secondDuration)<=a?(this.interval=1,this.intervalType="second"):c/(2*S.secondDuration)<=a?(this.interval=2,this.intervalType="second"):c/(5*S.secondDuration)<=a?(this.interval=5,this.intervalType="second"):c/(10*S.secondDuration)<=a?(this.interval= +10,this.intervalType="second"):c/(15*S.secondDuration)<=a?(this.interval=15,this.intervalType="second"):c/(20*S.secondDuration)<=a?(this.interval=20,this.intervalType="second"):c/(30*S.secondDuration)<=a?(this.interval=30,this.intervalType="second"):c/(1*S.minuteDuration)<=a?(this.interval=1,this.intervalType="minute"):c/(2*S.minuteDuration)<=a?(this.interval=2,this.intervalType="minute"):c/(5*S.minuteDuration)<=a?(this.interval=5,this.intervalType="minute"):c/(10*S.minuteDuration)<=a?(this.interval= +10,this.intervalType="minute"):c/(15*S.minuteDuration)<=a?(this.interval=15,this.intervalType="minute"):c/(20*S.minuteDuration)<=a?(this.interval=20,this.intervalType="minute"):c/(30*S.minuteDuration)<=a?(this.interval=30,this.intervalType="minute"):c/(1*S.hourDuration)<=a?(this.interval=1,this.intervalType="hour"):c/(2*S.hourDuration)<=a?(this.interval=2,this.intervalType="hour"):c/(3*S.hourDuration)<=a?(this.interval=3,this.intervalType="hour"):c/(6*S.hourDuration)<=a?(this.interval=6,this.intervalType= +"hour"):c/(1*S.dayDuration)<=a?(this.interval=1,this.intervalType="day"):c/(2*S.dayDuration)<=a?(this.interval=2,this.intervalType="day"):c/(4*S.dayDuration)<=a?(this.interval=4,this.intervalType="day"):c/(1*S.weekDuration)<=a?(this.interval=1,this.intervalType="week"):c/(2*S.weekDuration)<=a?(this.interval=2,this.intervalType="week"):c/(3*S.weekDuration)<=a?(this.interval=3,this.intervalType="week"):c/(1*S.monthDuration)<=a?(this.interval=1,this.intervalType="month"):c/(2*S.monthDuration)<=a?(this.interval= +2,this.intervalType="month"):c/(3*S.monthDuration)<=a?(this.interval=3,this.intervalType="month"):c/(6*S.monthDuration)<=a?(this.interval=6,this.intervalType="month"):(this.interval=c/(1*S.yearDuration)<=a?1:c/(2*S.yearDuration)<=a?2:c/(4*S.yearDuration)<=a?4:Math.floor(z.getNiceNumber(c/(a-1),!0)/S.yearDuration),this.intervalType="year"));if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=e-m/2;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum= +g+m/2;d?this.autoValueFormatString="MMM DD YYYY HH:mm":"year"===this.intervalType?this.autoValueFormatString="YYYY":"month"===this.intervalType?this.autoValueFormatString="MMM YYYY":"week"===this.intervalType?this.autoValueFormatString="MMM DD YYYY":"day"===this.intervalType?this.autoValueFormatString="MMM DD YYYY":"hour"===this.intervalType?this.autoValueFormatString="hh:mm TT":"minute"===this.intervalType?this.autoValueFormatString="hh:mm TT":"second"===this.intervalType?this.autoValueFormatString= +"hh:mm:ss TT":"millisecond"===this.intervalType&&(this.autoValueFormatString="fff'ms'");this.valueFormatString||(this.valueFormatString=this.autoValueFormatString)}else{this.intervalType="number";c=z.getNiceNumber(c,!1);this.interval=this.options&&0g?(c=Math.min(0.01*Math.abs(this.getApparentDifference(g,e,null,!0)),5),0<=g?e=g-c:g=isFinite(e)?e+c:0):(c=Math.min(0.01*Math.abs(this.getApparentDifference(e,g,null,!0)),0.05),0!==g&&(g+=c),0!==e&&(e-=c)):(g="undefined"===typeof this.options.interval?-Infinity:this.options.interval,e="undefined"!==typeof this.options.interval||isFinite(this.dataInfo.minDiff)?0:Infinity),m=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1g&&(g=0)),Math.abs(this.getApparentDifference(e,g,null,!0)),"axisX"===this.type&&b){this.valueType="dateTime";if(null===this.minimum||isNaN(this.minimum))this.minimum=e-m/2;if(null===this.maximum||isNaN(this.maximum))this.maximum=g+m/2}else this.intervalType=this.valueType="number",null===this.minimum&&(this.minimum="axisX"===this.type?e-m/2:Math.floor(e/this.interval)*this.interval,this.minimum=Math.min(this.minimum, +null===this.sessionVariables.viewportMinimum||isNaN(this.sessionVariables.viewportMinimum)?Infinity:this.sessionVariables.viewportMinimum)),null===this.maximum&&(this.maximum="axisX"===this.type?g+m/2:Math.ceil(g/this.interval)*this.interval,this.maximum=Math.max(this.maximum,null===this.sessionVariables.viewportMaximum||isNaN(this.sessionVariables.viewportMaximum)?-Infinity:this.sessionVariables.viewportMaximum)),0===this.maximum&&0===this.minimum&&(0===this.options.minimum?this.maximum+=10:0=== +this.options.maximum&&(this.minimum-=10));u(this.sessionVariables.newViewportMinimum)&&(this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum));u(this.sessionVariables.newViewportMaximum)&&(this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum));this.range=this.viewportMaximum-this.viewportMinimum;this.intervalStartPosition="axisX"===this.type&&b?this.getLabelStartPoint(new Date(this.viewportMinimum),this.intervalType,this.interval):Math.floor((this.viewportMinimum+0.2*this.interval)/ +this.interval)*this.interval;this.valueFormatString||(this.valueFormatString=z.generateValueFormatString(this.range,2))}};z.prototype.calculateLogarithmicAxisParameters=function(){var a=this.chart.layoutManager.getFreeSpace(),d=Math.log(this.logarithmBase),b;"bottom"===this._position||"top"===this._position?(this.maxWidth=a.width,this.maxHeight=a.height):(this.maxWidth=a.height,this.maxHeight=a.width);var a="axisX"===this.type?500>this.maxWidth?7:Math.max(7,Math.floor(this.maxWidth/100)):Math.max(Math.floor(this.maxWidth/ +50),3),c,e,g,m;m=1;if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=this.minimum;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;if(this.scaleBreaks)for(m=0;m=this.scaleBreaks._appliedBreaks[m].startValue||!u(this.options.minimum)&&this.options.minimum>=this.scaleBreaks._appliedBreaks[m].startValue|| +!u(this.options.viewportMinimum)&&this.viewportMinimum>=this.scaleBreaks._appliedBreaks[m].startValue)&&(!u(this.sessionVariables.newViewportMaximum)&&this.sessionVariables.newViewportMaximum<=this.scaleBreaks._appliedBreaks[m].endValue||!u(this.options.maximum)&&this.options.maximum<=this.scaleBreaks._appliedBreaks[m].endValue||!u(this.options.viewportMaximum)&&this.viewportMaximum<=this.scaleBreaks._appliedBreaks[m].endValue)){this.scaleBreaks._appliedBreaks.splice(m,1);break}"axisX"===this.type? +(c=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,e=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,1===e/c&&(m=Math.pow(this.logarithmBase,"undefined"===typeof this.options.interval?0.4:this.options.interval),e*=m,c/=m),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(c=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin, +e=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,0>=c&&!isFinite(e)?(e="undefined"===typeof this.options.interval?0:this.options.interval,c=1):0>=c?c=e:isFinite(e)||(e=c),1===c&&1===e?(e*=this.logarithmBase-1/this.logarithmBase,c=1):1===e/c?(m=Math.min(e*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),e*=m,c/=m):c>e?(m=Math.min(c/e*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),1<=e?c=e/m:e=c*m):(m=Math.min(e/c*Math.pow(this.logarithmBase, +0.01),Math.pow(this.logarithmBase,0.04)),1!==e&&(e*=m),1!==c&&(c/=m)),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.viewportMinimum||isNaN(this.viewportMinimum))&&1e&&(e=1));m=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?e:this.viewportMaximum)/(isNaN(this.viewportMinimum)||null=== +this.viewportMinimum?c:this.viewportMinimum);var l=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?e:this.viewportMaximum)-(isNaN(this.viewportMinimum)||null===this.viewportMinimum?c:this.viewportMinimum);this.intervalType="number";m=Math.pow(this.logarithmBase,z.getNiceNumber(Math.abs(Math.log(m)/d),!1));this.options&&0this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(c=null!==this.minimum?this.minimum:this.dataInfo.min,e=null!==this.maximum?this.maximum:this.dataInfo.max,isFinite(c)||isFinite(e)?1===c&&1===e?(e*=this.logarithmBase,c/=this.logarithmBase):1===e/c?(m=Math.pow(this.logarithmBase,this.interval),e*=m,c/=m):c>e?(m= +Math.min(0.01*(c/e),5),1<=e?c=e/m:e=c*m):(m=Math.min(e/c*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,0.04)),1!==e&&(e*=m),1!==c&&(c/=m)):(e="undefined"===typeof this.options.interval?0:this.options.interval,c=1),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.minimum||isNaN(this.minimum))&&1e&&(e=1)),this.intervalType="number",null===this.minimum&&(this.minimum="axisX"===this.type?c/Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.floor(Math.log(c)/d/this.interval)),this.minimum=Math.min(this.minimum,null===this.sessionVariables.viewportMinimum||isNaN(this.sessionVariables.viewportMinimum)?"undefined"===typeof this.sessionVariables.newViewportMinimum?Infinity:this.sessionVariables.newViewportMinimum:this.sessionVariables.viewportMinimum)),null===this.maximum&&(this.maximum= +"axisX"===this.type?e*Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.ceil(Math.log(e)/d/this.interval)),this.maximum=Math.max(this.maximum,null===this.sessionVariables.viewportMaximum||isNaN(this.sessionVariables.viewportMaximum)?"undefined"===typeof this.sessionVariables.newViewportMaximum?0:this.sessionVariables.newViewportMaximum:this.sessionVariables.viewportMaximum)),1===this.maximum&&1===this.minimum&&(1===this.options.minimum?this.maximum*=this.logarithmBase-1/this.logarithmBase: +1===this.options.maximum&&(this.minimum/=this.logarithmBase-1/this.logarithmBase));this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum);this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum);this.viewportMinimum>this.viewportMaximum&&(!this.options.viewportMinimum&&!this.options.minimum||this.options.viewportMaximum||this.options.maximum?this.options.viewportMinimum||this.options.minimum||!this.options.viewportMaximum&&!this.options.maximum||(this.viewportMinimum=this.minimum= +(this.options.viewportMaximum||this.options.maximum)/Math.pow(this.logarithmBase,2*Math.ceil(this.interval))):this.viewportMaximum=this.maximum=this.options.viewportMinimum||this.options.minimum);c=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(d*this.interval)+0.2)*this.interval);this.range=this.viewportMaximum/this.viewportMinimum;this.noTicks=a;if(!this.options.interval&&this.rangethis.viewportMaximum||3>a?2:3)){for(d=Math.floor(this.viewportMinimum/ +b+0.5)*b;dthis.interval&&(this.interval=b,c=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(d*this.interval)+0.2)*this.interval))),this.equidistantInterval=!0,this.intervalStartPosition=c;if(!this.valueFormatString&&(this.valueFormatString="#,##0.##",1>this.viewportMinimum)){d=Math.floor(Math.abs(Math.log(this.viewportMinimum)/ +Math.LN10))+2;if(isNaN(d)||!isFinite(d))d=2;if(2a&&(c+=Math.floor(Math.abs(Math.log(a)/Math.LN10)),isNaN(c)||!isFinite(c))&&(c=d);for(var e=0;eb?1>=c?1:5>=c?5:10:Math.max(Math.floor(c),1);return-20>b?Number(c*Math.pow(10,b)):Number((c*Math.pow(10,b)).toFixed(20))};z.getNiceNumber= +function(a,d){var b=Math.floor(Math.log(a)/Math.LN10),c=a/Math.pow(10,b),c=d?1.5>c?1:3>c?2:7>c?5:10:1>=c?1:2>=c?2:5>=c?5:10;return-20>b?Number(c*Math.pow(10,b)):Number((c*Math.pow(10,b)).toFixed(20))};z.prototype.getLabelStartPoint=function(){var a=S[this.intervalType+"Duration"]*this.interval,a=new Date(Math.floor(this.viewportMinimum/a)*a);if("millisecond"!==this.intervalType)if("second"===this.intervalType)0=a||"bottom"===this.scaleBreaks.parent._position&&0<=a)this.ctx.lineTo(c,l),this.ctx.lineTo(m,l),this.ctx.lineTo(m,e);else if("wavy"===this.type){k=c;h=e;g=0.5;p=(l-h)/a/3;for(var n=0;n=a||"right"===this.scaleBreaks.parent._position&&0<=a)this.ctx.lineTo(m,e),this.ctx.lineTo(m,l), +this.ctx.lineTo(c,l);else if("wavy"===this.type){k=c;h=e;g=0.5;p=(m-k)/a/3;for(n=0;nthis.parent.range?2:Math.floor(Math.abs(Math.log(this.parent.range)/Math.LN10))+(5>this.parent.range?2:10>this.parent.range? +1:0):50this.parent.range?2:10>this.parent.range?1:0);this.valueFormatString=z.generateValueFormatString(this.parent.range,h)}var l=null===this.opacity?1:this.opacity,h=Math.abs("pixel"===this._thicknessType?this.thickness:this.parent.conversionParameters.pixelPerUnit*this.thickness),p=this.chart.overlaidCanvasCtx,q=p.globalAlpha;p.globalAlpha=l;p.beginPath();p.strokeStyle=this.color;p.lineWidth=h;p.save();this.labelFontSize= +u(this.options.labelFontSize)?this.parent.labelFontSize:this.labelFontSize;if("left"===this.parent._position||"right"===this.parent._position)this.labelMaxWidth=u(this.options.labelMaxWidth)?this.parent.bounds.x2-this.parent.bounds.x1:this.labelMaxWidth,this.labelMaxHeight=u(this.options.labelWrap)||this.labelWrap?3*this.chart.height:2*this.labelFontSize;else if("top"===this.parent._position||"bottom"===this.parent._position)this.labelMaxWidth=u(this.options.labelMaxWidth)?3*this.chart.width:this.labelMaxWidth, +this.labelMaxHeight=u(this.options.labelWrap)||this.labelWrap?this.parent.bounds.height:2*this.labelFontSize;0this.chart.bounds.x2?l.x=this.chart.bounds.x2-l.width:l.xthis.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}}else if("bottom"===this.parent._position||"top"===this.parent._position){n=this.parent.convertPixelToValue({x:a});for(r=0;rthis.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width);l.xthis.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"=== +this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}m=null;("bottom"===this.parent._position||"top"===this.parent._position)&&(b>=this.parent.convertValueToPixel(this.parent.viewportMinimum)&&c<=this.parent.convertValueToPixel(this.parent.viewportMaximum))&&(0=this.parent.convertValueToPixel(this.parent.viewportMaximum)&& +e<=this.parent.convertValueToPixel(this.parent.viewportMinimum))&&(0this.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x1-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}else{if("bottom"===this.parent._position||"top"===this.parent._position)l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(a)}):u(this.options.label)? +ba(this.parent.convertPixelToValue(a),this.valueFormatString,this.chart._cultureInfo):this.label,l.x=b-l.measureText().width/2,l.x+l.width>this.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width),l.xthis.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width);l.xthis.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2),"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2);0(new Date).getTime()-this._lastUpdated||(this._lastUpdated=(new Date).getTime(), +this.chart.resetOverlayedCanvas(),this._updateToolTip(a,d))};$.prototype._updateToolTip=function(a,d,b){b="undefined"===typeof b?!0:b;this.container||this._initialize();this.enabled||this.hide();if(!this.chart.disableToolTip){if("undefined"===typeof a||"undefined"===typeof d){if(isNaN(this._prevX)||isNaN(this._prevY))return;a=this._prevX;d=this._prevY}else this._prevX=a,this._prevY=d;var c=null,e=null,g=[],k=0;if(this.shared&&this.enabled&&"none"!==this.chart.plotInfo.axisPlacement){if("xySwapped"=== +this.chart.plotInfo.axisPlacement){var l=[];if(this.chart.axisX)for(var p=0;ph.dataSeries.axisY.viewportMaximum&&b++;b-h.dataPoint.y.length&&g.push(h)}else"column"===e.type||"bar"===e.type?0>h.dataPoint.y?0>h.dataSeries.axisY.viewportMinimum&&h.dataSeries.axisY.viewportMaximum>=h.dataPoint.y&&g.push(h):h.dataSeries.axisY.viewportMinimum<=h.dataPoint.y&&0<=h.dataSeries.axisY.viewportMaximum&&g.push(h):"bubble"===e.type?(b=this.chart._eventManager.objectMap[e.dataPointIds[h.index]].size/2,h.dataPoint.y>= +h.dataSeries.axisY.viewportMinimum-b&&h.dataPoint.y<=h.dataSeries.axisY.viewportMaximum+b&&g.push(h)):"waterfall"===e.type?(b=0,h.cumulativeSumYStartValueh.dataSeries.axisY.viewportMaximum&&b++,h.cumulativeSumh.dataSeries.axisY.viewportMaximum&&b++,2>b&&-2=h.dataSeries.axisY.viewportMinimum&& +h.dataPoint.y<=h.dataSeries.axisY.viewportMaximum)&&g.push(h);else g.push(h)}}if(0a&&(a+=this.container.clientWidth+20);a+this.container.clientWidth> +Math.max(this.chart.container.clientWidth,this.chart.width)&&(a=Math.max(0,Math.max(this.chart.container.clientWidth,this.chart.width)-this.container.clientWidth));d=1!==g.length||this.shared||"line"!==g[0].dataSeries.type&&"stepLine"!==g[0].dataSeries.type&&"spline"!==g[0].dataSeries.type&&"area"!==g[0].dataSeries.type&&"stepArea"!==g[0].dataSeries.type&&"splineArea"!==g[0].dataSeries.type?"bar"===g[0].dataSeries.type||"rangeBar"===g[0].dataSeries.type||"stackedBar"===g[0].dataSeries.type||"stackedBar100"=== +g[0].dataSeries.type?g[0].dataSeries.axisX.convertValueToPixel(g[0].dataPoint.x):d:g[0].dataSeries.axisY.convertValueToPixel(g[0].dataPoint.y);d=-d+10;0":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content? +this.content:"{name}:  {y}",p=b.axisXIndex):"bubble"===b.type?(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:  {y},   {z}"): +"rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"error"===b.type?(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:  {y[0]}, {y[1]}"):"candlestick"=== +b.type||"ohlc"===b.type?(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:
Open:   {y[0]}
High:    {y[1]}
Low:   {y[2]}
Close:   {y[3]}"):"boxAndWhisker"=== +b.type&&(this.chart.axisX&&1":"X:{axisXIndex}
":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"{name}:
Minimum:   {y[0]}
Q1:               {y[1]}
Q2:               {y[4]}
Q3:               {y[2]}
Maximum:  {y[3]}"), +null===d&&(d=""),!0===this.reversed?(d=this.chart.replaceKeywordsWithValue(g,c,b,e)+d,l"+d)):(d+=this.chart.replaceKeywordsWithValue(g,c,b,e),l")));null!==d&&(d=h+d)}else{b=a[0].dataSeries;c=a[0].dataPoint;e=a[0].index;if(null===c.toolTipContent||"undefined"===typeof c.toolTipContent&&null===b.options.toolTipContent)return null;"line"===b.type||"stepLine"===b.type||"spline"===b.type||"area"===b.type||"stepArea"===b.type||"splineArea"===b.type||"column"=== +b.type||"bar"===b.type||"scatter"===b.type||"stackedColumn"===b.type||"stackedColumn100"===b.type||"stackedBar"===b.type||"stackedBar100"===b.type||"stackedArea"===b.type||"stackedArea100"===b.type||"waterfall"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+":  {y}":"bubble"===b.type?g=c.toolTipContent? +c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+":  {y},   {z}":"pie"===b.type||"doughnut"===b.type||"funnel"===b.type||"pyramid"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.name?"{name}:  ":c.label?"{label}:  ":"")+"{y}":"rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"error"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+" :  {y[0]},  {y[1]}": +"candlestick"===b.type||"ohlc"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+"
Open:   {y[0]}
High:    {y[1]}
Low:     {y[2]}
Close:   {y[3]}":"boxAndWhisker"===b.type&&(g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent: +this.content&&"function"!==typeof this.content?this.content:""+(c.label?"{label}":"{x}")+"
Minimum:   {y[0]}
Q1:               {y[1]}
Q2:               {y[4]}
Q3:               {y[2]}
Maximum:  {y[3]}"); +null===d&&(d="");d+=this.chart.replaceKeywordsWithValue(g,c,b,e)}return d};$.prototype.enableAnimation=function(){if(!this.container.style.WebkitTransition){var a=this.getContainerTransition(this.containerTransitionDuration);this.container.style.WebkitTransition=a;this.container.style.MsTransition=a;this.container.style.transition=a;this.container.style.MozTransition=this.mozContainerTransition}};$.prototype.disableAnimation=function(){this.container.style.WebkitTransition&&(this.container.style.WebkitTransition= +"",this.container.style.MozTransition="",this.container.style.MsTransition="",this.container.style.transition="")};$.prototype.hide=function(a){this.container&&(this.container.style.display="none",this.currentSeriesIndex=-1,this._prevY=this._prevX=NaN,("undefined"===typeof a||a)&&this.chart.resetOverlayedCanvas())};$.prototype.show=function(a,d,b){this._updateToolTip(a,d,"undefined"===typeof b?!1:b)};$.prototype.fixMozTransitionDelay=function(a,d){if(20c&&a.push(d),d.animationCallback(c),1<=c&&d.onComplete)d.onComplete();this.animations=a;0g;g++)for(var e=0;3>e;e++){for(var f=0,d=0;3>d;d++)f+=a[g][d]*b[d][e];c[g][e]=f}return c}function P(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX= +a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.globalAlpha=a.globalAlpha;b.font=a.font;b.textAlign=a.textAlign;b.textBaseline=a.textBaseline;b.arcScaleX_=a.arcScaleX_;b.arcScaleY_=a.arcScaleY_;b.lineScale_=a.lineScale_}function Q(a){var b=a.indexOf("(",3),c=a.indexOf(")",b+1),b=a.substring(b+1,c).split(",");if(4!=b.length||"a"!=a.charAt(3))b[3]=1;return b}function E(a,b,c){return Math.min(c,Math.max(b,a))}function F(a,b,c){0>c&&c++;16*c?a+6*(b-a)*c: +1>2*c?b:2>3*c?a+6*(b-a)*(2/3-c):a}function G(a){if(a in H)return H[a];var b,c=1;a=String(a);if("#"==a.charAt(0))b=a;else if(/^rgb/.test(a)){c=Q(a);b="#";for(var g,e=0;3>e;e++)g=-1!=c[e].indexOf("%")?Math.floor(255*(parseFloat(c[e])/100)):+c[e],b+=v[E(g,0,255)];c=+c[3]}else if(/^hsl/.test(a)){e=c=Q(a);b=parseFloat(e[0])/360%360;0>b&&b++;g=E(parseFloat(e[1])/100,0,1);e=E(parseFloat(e[2])/100,0,1);if(0==g)g=e=b=e;else{var f=0.5>e?e*(1+g):e+g-e*g,d=2*e-f;g=F(d,f,b+1/3);e=F(d,f,b);b=F(d,f,b-1/3)}b="#"+ +v[Math.floor(255*g)]+v[Math.floor(255*e)]+v[Math.floor(255*b)];c=c[3]}else b=Z[a]||a;return H[a]={color:b,alpha:c}}function C(a){this.m_=D();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=1*q;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=a;var b="width:"+a.clientWidth+"px;height:"+a.clientHeight+"px;overflow:hidden;position:absolute", +c=a.ownerDocument.createElement("div");c.style.cssText=b;a.appendChild(c);b=c.cloneNode(!1);b.style.backgroundColor="red";b.style.filter="alpha(opacity=0)";a.appendChild(b);this.element_=c;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}function R(a,b,c,g){a.currentPath_.push({type:"bezierCurveTo",cp1x:b.x,cp1y:b.y,cp2x:c.x,cp2y:c.y,x:g.x,y:g.y});a.currentX_=g.x;a.currentY_=g.y}function S(a,b){var c=G(a.strokeStyle),g=c.color,c=c.alpha*a.globalAlpha,e=a.lineScale_*a.lineWidth;1>e&&(c*=e);b.push("')}function T(a,b,c,g){var e=a.fillStyle,f=a.arcScaleX_,d=a.arcScaleY_,k=g.x-c.x,n=g.y-c.y;if(e instanceof w){var h=0,l=g=0,u=0,m=1;if("gradient"==e.type_){h=e.x1_/f;c=e.y1_/d;var p=s(a,e.x0_/f,e.y0_/d),h=s(a,h,c),h=180*Math.atan2(h.x-p.x,h.y-p.y)/Math.PI;0>h&&(h+=360);1E-6>h&&(h=0)}else p=s(a,e.x0_,e.y0_),g=(p.x-c.x)/k,l=(p.y-c.y)/n,k/=f*q, +n/=d*q,m=x.max(k,n),u=2*e.r0_/m,m=2*e.r1_/m-u;f=e.colors_;f.sort(function(a,b){return a.offset-b.offset});d=f.length;p=f[0].color;c=f[d-1].color;k=f[0].alpha*a.globalAlpha;a=f[d-1].alpha*a.globalAlpha;for(var n=[],r=0;r')}else e instanceof +I?k&&n&&b.push("'):(e=G(a.fillStyle),b.push(''))}function s(a,b,c){a=a.m_;return{x:q*(b*a[0][0]+c*a[1][0]+a[2][0])-r,y:q*(b*a[0][1]+c*a[1][1]+a[2][1])-r}}function z(a,b,c){isFinite(b[0][0])&&(isFinite(b[0][1])&&isFinite(b[1][0])&&isFinite(b[1][1])&&isFinite(b[2][0])&&isFinite(b[2][1]))&&(a.m_=b,c&&(a.lineScale_=aa(ba(b[0][0]*b[1][1]-b[0][1]* +b[1][0]))))}function w(a){this.type_=a;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}function I(a,b){if(!a||1!=a.nodeType||"IMG"!=a.tagName)throw new A("TYPE_MISMATCH_ERR");if("complete"!=a.readyState)throw new A("INVALID_STATE_ERR");switch(b){case "repeat":case null:case "":this.repetition_="repeat";break;case "repeat-x":case "repeat-y":case "no-repeat":this.repetition_=b;break;default:throw new A("SYNTAX_ERR");}this.src_=a.src;this.width_=a.width;this.height_=a.height} +function A(a){this.code=this[a];this.message=a+": DOM Exception "+this.code}var x=Math,k=x.round,J=x.sin,K=x.cos,ba=x.abs,aa=x.sqrt,q=10,r=q/2;navigator.userAgent.match(/MSIE ([\d.]+)?/);var M=Array.prototype.slice;O(document);var U={init:function(a){a=a||document;a.createElement("canvas");a.attachEvent("onreadystatechange",W(this.init_,this,a))},init_:function(a){a=a.getElementsByTagName("canvas");for(var b=0;bd;d++)for(var B=0;16>B;B++)v[16*d+B]=d.toString(16)+B.toString(16);var Z={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC", +bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000", +darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082", +ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA", +mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5", +peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"}, +H={},L={},$={butt:"flat",round:"round"},d=C.prototype;d.clearRect=function(){this.textMeasureEl_&&(this.textMeasureEl_.removeNode(!0),this.textMeasureEl_=null);this.element_.innerHTML=""};d.beginPath=function(){this.currentPath_=[]};d.moveTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.lineTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.bezierCurveTo= +function(a,b,c,g,e,f){e=s(this,e,f);a=s(this,a,b);c=s(this,c,g);R(this,a,c,e)};d.quadraticCurveTo=function(a,b,c,g){a=s(this,a,b);c=s(this,c,g);g={x:this.currentX_+2/3*(a.x-this.currentX_),y:this.currentY_+2/3*(a.y-this.currentY_)};R(this,g,{x:g.x+(c.x-this.currentX_)/3,y:g.y+(c.y-this.currentY_)/3},c)};d.arc=function(a,b,c,g,e,f){c*=q;var d=f?"at":"wa",k=a+K(g)*c-r,n=b+J(g)*c-r;g=a+K(e)*c-r;e=b+J(e)*c-r;k!=g||f||(k+=0.125);a=s(this,a,b);k=s(this,k,n);g=s(this,g,e);this.currentPath_.push({type:d, +x:a.x,y:a.y,radius:c,xStart:k.x,yStart:k.y,xEnd:g.x,yEnd:g.y})};d.rect=function(a,b,c,g){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath()};d.strokeRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath();this.stroke();this.currentPath_=e};d.fillRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+ +c,b+g);this.lineTo(a,b+g);this.closePath();this.fill();this.currentPath_=e};d.createLinearGradient=function(a,b,c,g){var e=new w("gradient");e.x0_=a;e.y0_=b;e.x1_=c;e.y1_=g;return e};d.createRadialGradient=function(a,b,c,g,e,f){var d=new w("gradientradial");d.x0_=a;d.y0_=b;d.r0_=c;d.x1_=g;d.y1_=e;d.r1_=f;return d};d.drawImage=function(a,b){var c,g,e,d,r,y,n,h;e=a.runtimeStyle.width;d=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var l=a.width,u=a.height;a.runtimeStyle.width= +e;a.runtimeStyle.height=d;if(3==arguments.length)c=arguments[1],g=arguments[2],r=y=0,n=e=l,h=d=u;else if(5==arguments.length)c=arguments[1],g=arguments[2],e=arguments[3],d=arguments[4],r=y=0,n=l,h=u;else if(9==arguments.length)r=arguments[1],y=arguments[2],n=arguments[3],h=arguments[4],c=arguments[5],g=arguments[6],e=arguments[7],d=arguments[8];else throw Error("Invalid number of arguments");var m=s(this,c,g),p=[];p.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",p.join(""))};d.stroke=function(a){var b=[];b.push("d.x)d.x=f.x;if(null==c.y||f.yd.y)d.y=f.y}}b.push(' ">');a?T(this,b,c,d):S(this,b);b.push("");this.element_.insertAdjacentHTML("beforeEnd",b.join(""))};d.fill=function(){this.stroke(!0)};d.closePath=function(){this.currentPath_.push({type:"close"})};d.save=function(){var a= +{};P(this,a);this.aStack_.push(a);this.mStack_.push(this.m_);this.m_=t(D(),this.m_)};d.restore=function(){this.aStack_.length&&(P(this.aStack_.pop(),this),this.m_=this.mStack_.pop())};d.translate=function(a,b){z(this,t([[1,0,0],[0,1,0],[a,b,1]],this.m_),!1)};d.rotate=function(a){var b=K(a);a=J(a);z(this,t([[b,a,0],[-a,b,0],[0,0,1]],this.m_),!1)};d.scale=function(a,b){this.arcScaleX_*=a;this.arcScaleY_*=b;z(this,t([[a,0,0],[0,b,0],[0,0,1]],this.m_),!0)};d.transform=function(a,b,c,d,e,f){z(this,t([[a, +b,0],[c,d,0],[e,f,1]],this.m_),!0)};d.setTransform=function(a,b,c,d,e,f){z(this,[[a,b,0],[c,d,0],[e,f,1]],!0)};d.drawText_=function(a,b,c,d,e){var f=this.m_;d=0;var r=1E3,t=0,n=[],h;h=this.font;if(L[h])h=L[h];else{var l=document.createElement("div").style;try{l.font=h}catch(u){}h=L[h]={style:l.fontStyle||"normal",variant:l.fontVariant||"normal",weight:l.fontWeight||"normal",size:l.fontSize||10,family:l.fontFamily||"sans-serif"}}var l=h,m=this.element_;h={};for(var p in l)h[p]=l[p];p=parseFloat(m.currentStyle.fontSize); +m=parseFloat(l.size);"number"==typeof l.size?h.size=l.size:-1!=l.size.indexOf("px")?h.size=m:-1!=l.size.indexOf("em")?h.size=p*m:-1!=l.size.indexOf("%")?h.size=p/100*m:-1!=l.size.indexOf("pt")?h.size=m/0.75:h.size=p;h.size*=0.981;p=h.style+" "+h.variant+" "+h.weight+" "+h.size+"px "+h.family;m=this.element_.currentStyle;l=this.textAlign.toLowerCase();switch(l){case "left":case "center":case "right":break;case "end":l="ltr"==m.direction?"right":"left";break;case "start":l="rtl"==m.direction?"right": +"left";break;default:l="left"}switch(this.textBaseline){case "hanging":case "top":t=h.size/1.75;break;case "middle":break;default:case null:case "alphabetic":case "ideographic":case "bottom":t=-h.size/2.25}switch(l){case "right":d=1E3;r=0.05;break;case "center":d=r=500}b=s(this,b+0,c+t);n.push('');e?S(this,n):T(this,n,{x:-d,y:0}, +{x:r,y:h.size});e=f[0][0].toFixed(3)+","+f[1][0].toFixed(3)+","+f[0][1].toFixed(3)+","+f[1][1].toFixed(3)+",0,0";b=k(b.x/q)+","+k(b.y/q);n.push('','','');this.element_.insertAdjacentHTML("beforeEnd",n.join(""))};d.fillText=function(a,b,c,d){this.drawText_(a,b,c,d,!1)};d.strokeText=function(a, +b,c,d){this.drawText_(a,b,c,d,!0)};d.measureText=function(a){this.textMeasureEl_||(this.element_.insertAdjacentHTML("beforeEnd",''),this.textMeasureEl_=this.element_.lastChild);var b=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(b.createTextNode(a));return{width:this.textMeasureEl_.offsetWidth}};d.clip=function(){}; +d.arcTo=function(){};d.createPattern=function(a,b){return new I(a,b)};w.prototype.addColorStop=function(a,b){b=G(b);this.colors_.push({offset:a,color:b.color,alpha:b.alpha})};d=A.prototype=Error();d.INDEX_SIZE_ERR=1;d.DOMSTRING_SIZE_ERR=2;d.HIERARCHY_REQUEST_ERR=3;d.WRONG_DOCUMENT_ERR=4;d.INVALID_CHARACTER_ERR=5;d.NO_DATA_ALLOWED_ERR=6;d.NO_MODIFICATION_ALLOWED_ERR=7;d.NOT_FOUND_ERR=8;d.NOT_SUPPORTED_ERR=9;d.INUSE_ATTRIBUTE_ERR=10;d.INVALID_STATE_ERR=11;d.SYNTAX_ERR=12;d.INVALID_MODIFICATION_ERR= +13;d.NAMESPACE_ERR=14;d.INVALID_ACCESS_ERR=15;d.VALIDATION_ERR=16;d.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=U;CanvasRenderingContext2D=C;CanvasGradient=w;CanvasPattern=I;DOMException=A}(); + +/* + CanvasJS jQuery Charting Plugin - https://canvasjs.com/ + Copyright 2017 fenopix + + --------------------- License Information -------------------- + CanvasJS is a commercial product which requires purchase of license. Without a commercial license you can use it for evaluation purposes for upto 30 days. Please refer to the following link for further details. + https://canvasjs.com/license/ + +*/ +(function(b,c,d,e){b.fn.CanvasJSChart=function(a){if(a){var b=this.first();a=new CanvasJS.Chart(this[0],a);b.children(".canvasjs-chart-container").data("canvasjsChartRef",a);a.render();return this}return this.first().children(".canvasjs-chart-container").data("canvasjsChartRef")}})(jQuery,window,document); +/*eslint-enable*/ +/*jshint ignore:end*/ \ No newline at end of file diff --git a/frontend-syscop/src/canvasjs-2.3.1/license.txt b/frontend-syscop/src/canvasjs-2.3.1/license.txt new file mode 100644 index 0000000..f9882a1 --- /dev/null +++ b/frontend-syscop/src/canvasjs-2.3.1/license.txt @@ -0,0 +1,9 @@ +* +* @preserve CanvasJS HTML5 & JavaScript Charts - v2.3.1 GA - https://canvasjs.com/ +* Copyright 2018 fenopix +* +* --------------------- License Information -------------------- +* CanvasJS is a commercial product which requires purchase of license. Without a commercial license you can use it for evaluation purposes for upto 30 days. Please refer to the following link for further details. +* https://canvasjs.com/license/ +* +* \ No newline at end of file diff --git a/frontend-syscop/src/index.html b/frontend-syscop/src/index.html index c4c9440..18dcd43 100644 --- a/frontend-syscop/src/index.html +++ b/frontend-syscop/src/index.html @@ -8,6 +8,8 @@ + + From d396a29e3095409ad15441b879972b51ee6c533f Mon Sep 17 00:00:00 2001 From: sushantanshu Date: Thu, 14 Feb 2019 14:59:47 +0530 Subject: [PATCH 09/38] front-end --- .../src/app/home/home.component.html | 9 +++--- .../src/app/home/home.component.ts | 24 +++++++++++--- .../initial-view/initial-view.component.css | 5 +-- .../src/app/login/login.component.ts | 32 +------------------ frontend-syscop/src/app/user.service.ts | 2 ++ 5 files changed, 31 insertions(+), 41 deletions(-) diff --git a/frontend-syscop/src/app/home/home.component.html b/frontend-syscop/src/app/home/home.component.html index f22a881..2b47106 100644 --- a/frontend-syscop/src/app/home/home.component.html +++ b/frontend-syscop/src/app/home/home.component.html @@ -1,3 +1,4 @@ + trending_up Data Sources - - account_circle - Admin - work Company + + account_circle + Logout + diff --git a/frontend-syscop/src/app/home/home.component.ts b/frontend-syscop/src/app/home/home.component.ts index d863408..eda8d4a 100644 --- a/frontend-syscop/src/app/home/home.component.ts +++ b/frontend-syscop/src/app/home/home.component.ts @@ -1,20 +1,36 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { Router } from '@angular/router'; +import { TokenStorageService } from '../auth/token-storage.service'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) -export class HomeComponent { +export class HomeComponent implements OnInit { + info: any; isHandset$: Observable = this.breakpointObserver.observe(Breakpoints.Handset) .pipe( map(result => result.matches) ); - constructor(private breakpointObserver: BreakpointObserver) { + constructor(private breakpointObserver: BreakpointObserver, private router: Router, private token: TokenStorageService) { + } + ngOnInit() { + this.info = { + token: this.token.getToken(), + username: this.token.getUsername(), + authorities: this.token.getAuthorities() + }; + if (!this.info.token) { + this.router.navigateByUrl('login'); + } + } + logout() { + this.token.signOut(); + this.router.navigateByUrl(''); } - } diff --git a/frontend-syscop/src/app/initial-view/initial-view.component.css b/frontend-syscop/src/app/initial-view/initial-view.component.css index caacbf0..1ff32e5 100644 --- a/frontend-syscop/src/app/initial-view/initial-view.component.css +++ b/frontend-syscop/src/app/initial-view/initial-view.component.css @@ -12,7 +12,8 @@ /* Control the left side */ .left { left: 0; - background-color: deepskyblue; + background-color: rgba(20,30,45,1); + color: white; } /* Control the right side */ @@ -30,7 +31,7 @@ text-align: center; } .log,.sign{ - background: deepskyblue; + background: rgba(20,30,45,1); color:white; width: 25rem; height:2rem; diff --git a/frontend-syscop/src/app/login/login.component.ts b/frontend-syscop/src/app/login/login.component.ts index 46c6e1f..83975aa 100644 --- a/frontend-syscop/src/app/login/login.component.ts +++ b/frontend-syscop/src/app/login/login.component.ts @@ -1,34 +1,4 @@ -// import { Component, OnInit } from '@angular/core'; -// import { FormGroup, FormControl, Validators } from '@angular/forms'; -// import { UserService } from '../user.service'; -// @Component({ -// selector: 'app-login', -// templateUrl: './login.component.html', -// styleUrls: ['./login.component.css'] -// }) -// export class LoginComponent implements OnInit { - -// myForm: FormGroup; -// constructor(private userService: UserService) { } - -// ngOnInit() { -// this.myForm = new FormGroup({ -// username: new FormControl('', [Validators.required ]), -// password: new FormControl('', [Validators.required ]), -// }); -// } -// onSubmit() { -// // stop print if form is invalid -// if (this.myForm.invalid) { -// console.log('register form is invalid '); -// return; -// } -// console.log('registerForm.value : ', this.myForm.value); -// this.userService.saveUser(this.myForm.value); -// } - -// } import { Component, OnInit } from '@angular/core'; import { AuthService } from '../auth/auth.service'; @@ -84,7 +54,7 @@ export class LoginComponent implements OnInit { this.isLoginFailed = false; this.isLoggedIn = true; this.roles = this.tokenStorage.getAuthorities(); - this.router.navigateByUrl('/home'); + this.router.navigateByUrl('home'); }, error => { console.log(error); diff --git a/frontend-syscop/src/app/user.service.ts b/frontend-syscop/src/app/user.service.ts index e46d3e7..7b709dc 100644 --- a/frontend-syscop/src/app/user.service.ts +++ b/frontend-syscop/src/app/user.service.ts @@ -10,6 +10,8 @@ export class UserService { public url = 'http://172.23.239.170:8096/api/v1/register'; public userUrl = 'http://172.23.239.148:8090/api/test/user'; + + constructor(private http: HttpClient) { } saveUser(user: any) { this.http.post(this.url, user, {responseType: 'text'} ).subscribe((data) => { From 67f07bf6e3bf50c88ebdc8bf3d81a0ce54f24e48 Mon Sep 17 00:00:00 2001 From: Shri Date: Thu, 14 Feb 2019 16:19:35 +0530 Subject: [PATCH 10/38] Using api gateway --- frontend-syscop/Dockerfile | 44 +++++----- frontend-syscop/nginx/default.conf | 25 ++++++ frontend-syscop/package-lock.json | 82 ++++++------------- frontend-syscop/package.json | 1 + frontend-syscop/src/app/auth/auth.service.ts | 6 +- .../src/app/login/login.component.ts | 1 + .../src/app/signup/signup.component.ts | 1 + frontend-syscop/src/app/user.service.ts | 7 +- 8 files changed, 80 insertions(+), 87 deletions(-) create mode 100644 frontend-syscop/nginx/default.conf diff --git a/frontend-syscop/Dockerfile b/frontend-syscop/Dockerfile index 6c6389b..8bb2132 100644 --- a/frontend-syscop/Dockerfile +++ b/frontend-syscop/Dockerfile @@ -1,25 +1,31 @@ -# base image -FROM node:9.6.1 +### STAGE 1: Build ### -# install chrome for protractor tests -RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - -RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' -RUN apt-get update && apt-get install -yq google-chrome-stable --fix-missing +# We label our stage as ‘builder’ +FROM node:10-alpine as builder -# set working directory -RUN mkdir register-front -WORKDIR /register-front +COPY package.json package-lock.json ./ -# add `/usr/src/app/node_modules/.bin` to $PATH -ENV PATH /node_modules/.bin:$PATH +## Storing node modules on a separate layer will prevent unnecessary npm installs at each build +RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app -# install and cache app dependencies -COPY package.json /package.json -RUN npm install -RUN npm install -g @angular/cli@1.7.1 +WORKDIR /ng-app -# add app -COPY . /register-front +COPY . . -# start app -CMD ng serve --host 0.0.0.0 \ No newline at end of file +## Build the angular app in production mode and store the artifacts in dist folder +RUN $(npm bin)/ng build --prod --output-path=dist + + +### STAGE 2: Setup ### +FROM nginx:1.14.1-alpine + +## Copy our default nginx config +COPY nginx/default.conf /etc/nginx/conf.d/ + +## Remove default nginx website +RUN rm -rf /usr/share/nginx/html/* + +## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder +COPY --from=builder /ng-app/dist /usr/share/nginx/html + +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend-syscop/nginx/default.conf b/frontend-syscop/nginx/default.conf new file mode 100644 index 0000000..9d43bff --- /dev/null +++ b/frontend-syscop/nginx/default.conf @@ -0,0 +1,25 @@ +server { + + listen 8004; + + sendfile on; + + default_type application/octet-stream; + + + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 1100; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 9; + root /usr/share/nginx/html; + + + location / { + try_files $uri $uri/ /index.html =404; + } + +} diff --git a/frontend-syscop/package-lock.json b/frontend-syscop/package-lock.json index 6522574..2f5d4d4 100644 --- a/frontend-syscop/package-lock.json +++ b/frontend-syscop/package-lock.json @@ -1252,7 +1252,6 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, - "optional": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" @@ -2832,8 +2831,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true + "dev": true }, "constants-browserify": { "version": "1.0.0", @@ -3498,8 +3496,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true + "dev": true }, "depd": { "version": "1.1.2", @@ -4585,8 +4582,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4607,14 +4603,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4629,20 +4623,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4759,8 +4750,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4772,7 +4762,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4787,7 +4776,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4795,14 +4783,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4821,7 +4807,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4902,8 +4887,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4915,7 +4899,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5001,8 +4984,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5038,7 +5020,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5058,7 +5039,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5102,14 +5082,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -5118,7 +5096,6 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -5131,7 +5108,6 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, - "optional": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -5169,8 +5145,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true, - "optional": true + "dev": true }, "get-stream": { "version": "3.0.0", @@ -5398,8 +5373,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true + "dev": true }, "has-value": { "version": "1.0.0", @@ -6153,8 +6127,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true, - "optional": true + "dev": true }, "is-windows": { "version": "1.0.2", @@ -6493,6 +6466,11 @@ "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", "dev": true }, + "jquery": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz", + "integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==" + }, "js-base64": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", @@ -6828,7 +6806,6 @@ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", @@ -6841,8 +6818,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -7115,8 +7091,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "optional": true + "dev": true }, "map-visit": { "version": "1.0.0", @@ -7742,7 +7717,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, - "optional": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -8839,7 +8813,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, - "optional": true, "requires": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", @@ -8851,7 +8824,6 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, - "optional": true, "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -8862,8 +8834,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -8872,7 +8843,6 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, - "optional": true, "requires": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" @@ -8883,7 +8853,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, - "optional": true, "requires": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" @@ -8894,7 +8863,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, - "optional": true, "requires": { "pinkie-promise": "^2.0.0" } @@ -10193,7 +10161,6 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, - "optional": true, "requires": { "is-utf8": "^0.2.0" } @@ -11523,7 +11490,6 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "optional": true, "requires": { "string-width": "^1.0.2 || 2" } diff --git a/frontend-syscop/package.json b/frontend-syscop/package.json index 29f6128..a3c91c6 100644 --- a/frontend-syscop/package.json +++ b/frontend-syscop/package.json @@ -27,6 +27,7 @@ "core-js": "^2.5.4", "d3": "^5.9.1", "hammerjs": "^2.0.8", + "jquery": "^3.3.1", "rxjs": "~6.3.3", "tslib": "^1.9.0", "zone.js": "~0.8.26" diff --git a/frontend-syscop/src/app/auth/auth.service.ts b/frontend-syscop/src/app/auth/auth.service.ts index 525d202..da9a6c5 100644 --- a/frontend-syscop/src/app/auth/auth.service.ts +++ b/frontend-syscop/src/app/auth/auth.service.ts @@ -18,8 +18,7 @@ export class AuthService { // private loginUrl = 'http://13.232.165.99:8090/api/auth/signin'; // private signupUrl = 'http://13.232.165.99:8090/api/auth/signup'; - public loginUrl = 'http://172.23.239.148:8090/api/auth/signin'; - public signupUrl = 'http://172.23.239.148:8090/api/auth/signup'; + public loginUrl = 'http://localhost:8095/login-service/api/auth/signin'; constructor(private http: HttpClient) { } @@ -28,7 +27,4 @@ export class AuthService { return this.http.post(this.loginUrl, credentials, httpOptions); } - signUp(info: SignUpInfo): Observable { - return this.http.post(this.signupUrl, info, httpOptions); - } } diff --git a/frontend-syscop/src/app/login/login.component.ts b/frontend-syscop/src/app/login/login.component.ts index 83975aa..1047b50 100644 --- a/frontend-syscop/src/app/login/login.component.ts +++ b/frontend-syscop/src/app/login/login.component.ts @@ -13,6 +13,7 @@ import { Router } from '@angular/router'; styleUrls: ['./login.component.css'] }) export class LoginComponent implements OnInit { + hide; form: any = {}; isLoggedIn = false; isLoginFailed = false; diff --git a/frontend-syscop/src/app/signup/signup.component.ts b/frontend-syscop/src/app/signup/signup.component.ts index 30f5ca3..e96ee3e 100644 --- a/frontend-syscop/src/app/signup/signup.component.ts +++ b/frontend-syscop/src/app/signup/signup.component.ts @@ -8,6 +8,7 @@ import { FormControl, Validators, FormGroup } from '@angular/forms'; styleUrls: ['./signup.component.css'] }) export class SignupComponent implements OnInit { + hide; myForm: FormGroup; constructor(private userService: UserService) { } diff --git a/frontend-syscop/src/app/user.service.ts b/frontend-syscop/src/app/user.service.ts index 7b709dc..63dd5cc 100644 --- a/frontend-syscop/src/app/user.service.ts +++ b/frontend-syscop/src/app/user.service.ts @@ -7,8 +7,7 @@ import { Observable } from 'rxjs'; }) export class UserService { - public url = 'http://172.23.239.170:8096/api/v1/register'; - public userUrl = 'http://172.23.239.148:8090/api/test/user'; + public url = 'http://localhost:8095/register-service/api/v1/register'; @@ -20,9 +19,7 @@ export class UserService { console.log(user); } - getUserBoard(): Observable { - return this.http.get(this.userUrl, { responseType: 'text' }); - } + // getUsers(): Observable { // return this // .http From 2f4258e1db59e1d5eecabc9d07a909a8dafb67f3 Mon Sep 17 00:00:00 2001 From: Shri Date: Fri, 15 Feb 2019 08:55:49 +0530 Subject: [PATCH 11/38] Eureka and API gateway --- eureka-server/Dockerfile | 2 +- eureka-server/src/main/resources/application.properties | 4 ++-- frontend-syscop/proxy.config.json | 8 ++++++++ frontend-syscop/src/app/auth/auth.service.ts | 2 +- frontend-syscop/src/app/user.service.ts | 2 +- zuul-gatewayservice/Dockerfile | 2 +- .../zuulgatewayservice/ZuulGatewayserviceApplication.java | 3 ++- .../src/main/resources/application.properties | 7 ++++--- 8 files changed, 20 insertions(+), 10 deletions(-) create mode 100644 frontend-syscop/proxy.config.json diff --git a/eureka-server/Dockerfile b/eureka-server/Dockerfile index c2da879..33c3c5a 100644 --- a/eureka-server/Dockerfile +++ b/eureka-server/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:11 ADD ./target/eureka-server-0.0.1-SNAPSHOT.jar /event/src/eureka-server-0.0.1-SNAPSHOT.jar -EXPOSE 9091 +EXPOSE 8010 WORKDIR event/src ENTRYPOINT ["java","-jar","eureka-server-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/eureka-server/src/main/resources/application.properties b/eureka-server/src/main/resources/application.properties index 8031a44..62ab5f4 100644 --- a/eureka-server/src/main/resources/application.properties +++ b/eureka-server/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.application.name=EurekaServer -eureka.client.serviceUrl.defaultZone:http://localhost:9091/eureka/ -server.port=9091 +eureka.client.serviceUrl.defaultZone:http://localhost:8010/eureka/ +server.port=8010 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false diff --git a/frontend-syscop/proxy.config.json b/frontend-syscop/proxy.config.json new file mode 100644 index 0000000..6209303 --- /dev/null +++ b/frontend-syscop/proxy.config.json @@ -0,0 +1,8 @@ +{ + "/delivery/all/*": { + "target": "https://localhost:8095", + "secure": false, + "logLevel": "debug", + "changeOrigin": true + } +} \ No newline at end of file diff --git a/frontend-syscop/src/app/auth/auth.service.ts b/frontend-syscop/src/app/auth/auth.service.ts index da9a6c5..71d2b42 100644 --- a/frontend-syscop/src/app/auth/auth.service.ts +++ b/frontend-syscop/src/app/auth/auth.service.ts @@ -18,7 +18,7 @@ export class AuthService { // private loginUrl = 'http://13.232.165.99:8090/api/auth/signin'; // private signupUrl = 'http://13.232.165.99:8090/api/auth/signup'; - public loginUrl = 'http://localhost:8095/login-service/api/auth/signin'; + public loginUrl = 'http://13.232.165.99:8095/login-service/api/auth/signin'; constructor(private http: HttpClient) { } diff --git a/frontend-syscop/src/app/user.service.ts b/frontend-syscop/src/app/user.service.ts index 63dd5cc..f93e34d 100644 --- a/frontend-syscop/src/app/user.service.ts +++ b/frontend-syscop/src/app/user.service.ts @@ -7,7 +7,7 @@ import { Observable } from 'rxjs'; }) export class UserService { - public url = 'http://localhost:8095/register-service/api/v1/register'; + public url = 'http://13.232.165.99:8095/register-service/api/v1/register'; diff --git a/zuul-gatewayservice/Dockerfile b/zuul-gatewayservice/Dockerfile index 402ae5a..102dfb7 100644 --- a/zuul-gatewayservice/Dockerfile +++ b/zuul-gatewayservice/Dockerfile @@ -1,6 +1,6 @@ FROM openjdk:11 ADD ./target/zuul-gatewayservice-0.0.1-SNAPSHOT.jar /event/src/zuul-gatewayservice-0.0.1-SNAPSHOT.jar -EXPOSE 8092 +EXPOSE 8095 WORKDIR event/src ENTRYPOINT ["java","-jar","zuul-gatewayservice-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/zuul-gatewayservice/src/main/java/com/stackroute/zuulgatewayservice/ZuulGatewayserviceApplication.java b/zuul-gatewayservice/src/main/java/com/stackroute/zuulgatewayservice/ZuulGatewayserviceApplication.java index 42e1ca6..7da90b2 100644 --- a/zuul-gatewayservice/src/main/java/com/stackroute/zuulgatewayservice/ZuulGatewayserviceApplication.java +++ b/zuul-gatewayservice/src/main/java/com/stackroute/zuulgatewayservice/ZuulGatewayserviceApplication.java @@ -5,8 +5,9 @@ import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.CrossOrigin; - +@CrossOrigin("*") @EnableDiscoveryClient @EnableZuulProxy @SpringBootApplication diff --git a/zuul-gatewayservice/src/main/resources/application.properties b/zuul-gatewayservice/src/main/resources/application.properties index 367ac77..5cd1bc7 100644 --- a/zuul-gatewayservice/src/main/resources/application.properties +++ b/zuul-gatewayservice/src/main/resources/application.properties @@ -1,9 +1,10 @@ -server.port=8092 -zuul.routes.event-service.url=http://localhost:8082 +server.port=8095 +zuul.routes.login-service.url=http://localhost:8090 +zuul.routes.register-service.url=http://localhost:8096 #ribbon.eureka.enabled=false spring.application.name=api-gateway -eureka.client.serviceUrl.defaultZone=http://localhost:9091/eureka/ +eureka.client.serviceUrl.defaultZone=http://localhost:8010/eureka/ eureka.client.register-with-eureka=true eureka.client.fetch-registry=true From 1cf81726ed8124fe0d2cb174be739e1ca6d4dc2d Mon Sep 17 00:00:00 2001 From: Shri Date: Fri, 15 Feb 2019 09:46:17 +0530 Subject: [PATCH 12/38] Used http-server --- frontend-syscop/Dockerfile | 52 ++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/frontend-syscop/Dockerfile b/frontend-syscop/Dockerfile index 8bb2132..89f6cc6 100644 --- a/frontend-syscop/Dockerfile +++ b/frontend-syscop/Dockerfile @@ -1,31 +1,45 @@ -### STAGE 1: Build ### +#Using nginx +# ### STAGE 1: Build ### -# We label our stage as ‘builder’ -FROM node:10-alpine as builder +# # We label our stage as ‘builder’ +# FROM node:10-alpine as builder -COPY package.json package-lock.json ./ +# COPY package.json package-lock.json ./ -## Storing node modules on a separate layer will prevent unnecessary npm installs at each build -RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app +# ## Storing node modules on a separate layer will prevent unnecessary npm installs at each build +# RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app -WORKDIR /ng-app +# WORKDIR /ng-app -COPY . . +# COPY . . + +# ## Build the angular app in production mode and store the artifacts in dist folder +# RUN $(npm bin)/ng build --prod --output-path=dist -## Build the angular app in production mode and store the artifacts in dist folder -RUN $(npm bin)/ng build --prod --output-path=dist +# ### STAGE 2: Setup ### +# FROM nginx:1.14.1-alpine -### STAGE 2: Setup ### -FROM nginx:1.14.1-alpine +# ## Copy our default nginx config +# COPY nginx/default.conf /etc/nginx/conf.d/ -## Copy our default nginx config -COPY nginx/default.conf /etc/nginx/conf.d/ +# ## Remove default nginx website +# RUN rm -rf /usr/share/nginx/html/* -## Remove default nginx website -RUN rm -rf /usr/share/nginx/html/* +# ## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder +# COPY --from=builder /ng-app/dist /usr/share/nginx/html -## From ‘builder’ stage copy over the artifacts in dist folder to default nginx public folder -COPY --from=builder /ng-app/dist /usr/share/nginx/html +# CMD ["nginx", "-g", "daemon off;"] + +#Using http server +FROM node:9.6.1 as node +WORKDIR /usr/src +COPY package*.json ./ +RUN npm install +COPY . . +# RUN npm run build -CMD ["nginx", "-g", "daemon off;"] +# Stage 2 +FROM node +RUN npm install http-server -g +CMD ["http-server","-p","8004", "dist/register-front"] \ No newline at end of file From 3b739717a8c64e1c1e80dec77806748efd7d35df Mon Sep 17 00:00:00 2001 From: "Rohith.Prakash" Date: Fri, 15 Feb 2019 10:10:59 +0530 Subject: [PATCH 13/38] authentication service commit1 --- authentication-service/.gitignore | 25 ++++ authentication-service/Dockerfile | 5 + authentication-service/pom.xml | 107 ++++++++++++++++ .../JwtauthenticationApplication.java | 16 +++ .../config/KafkaConfiguration.java | 36 ++++++ .../config/SwaggerConfig.java | 22 ++++ .../controller/AuthRestAPIs.java | 116 ++++++++++++++++++ .../controller/TestRestAPIs.java | 18 +++ .../message/request/LoginForm.java | 30 +++++ .../message/request/SignUpForm.java | 66 ++++++++++ .../message/response/JwtResponse.java | 46 +++++++ .../message/response/ResponseMessage.java | 17 +++ .../jwtauthentication/model/Role.java | 47 +++++++ .../jwtauthentication/model/RoleName.java | 7 ++ .../jwtauthentication/model/User.java | 116 ++++++++++++++++++ .../repository/RoleRepository.java | 14 +++ .../repository/UserRepository.java | 16 +++ .../security/WebSecurityConfig.java | 68 ++++++++++ .../security/jwt/JwtAuthEntryPoint.java | 29 +++++ .../security/jwt/JwtAuthTokenFilter.java | 63 ++++++++++ .../security/jwt/JwtProvider.java | 62 ++++++++++ .../services/KafkaListenerService.java | 46 +++++++ .../services/UserDetailsServiceImpl.java | 27 ++++ .../security/services/UserPrinciple.java | 111 +++++++++++++++++ .../src/main/resources/application.properties | 33 +++++ .../src/main/resources/application.yml | 21 ++++ .../src/main/resources/roles.sql | 3 + .../JwtauthenticationApplicationTests.java | 19 +++ .../controller/AuthRestAPIsTest.java | 7 ++ 29 files changed, 1193 insertions(+) create mode 100644 authentication-service/.gitignore create mode 100644 authentication-service/Dockerfile create mode 100644 authentication-service/pom.xml create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/JwtauthenticationApplication.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/config/KafkaConfiguration.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/config/SwaggerConfig.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/AuthRestAPIs.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/TestRestAPIs.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/LoginForm.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/SignUpForm.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/JwtResponse.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/ResponseMessage.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/model/Role.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/model/RoleName.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/model/User.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/RoleRepository.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/UserRepository.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/WebSecurityConfig.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthEntryPoint.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthTokenFilter.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtProvider.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/KafkaListenerService.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserDetailsServiceImpl.java create mode 100644 authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserPrinciple.java create mode 100644 authentication-service/src/main/resources/application.properties create mode 100644 authentication-service/src/main/resources/application.yml create mode 100644 authentication-service/src/main/resources/roles.sql create mode 100644 authentication-service/src/test/java/com/grokonez/jwtauthentication/JwtauthenticationApplicationTests.java create mode 100644 authentication-service/src/test/java/com/grokonez/jwtauthentication/controller/AuthRestAPIsTest.java diff --git a/authentication-service/.gitignore b/authentication-service/.gitignore new file mode 100644 index 0000000..c456c4a --- /dev/null +++ b/authentication-service/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/authentication-service/Dockerfile b/authentication-service/Dockerfile new file mode 100644 index 0000000..f3dd9e9 --- /dev/null +++ b/authentication-service/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:11.0-oracle +ADD ./target/jwtauthentication-0.0.1-SNAPSHOT.jar /usr/src/jwtauthentication-0.0.1-SNAPSHOT.jar +WORKDIR usr/src +ENTRYPOINT ["java","-jar","jwtauthentication-0.0.1-SNAPSHOT.jar"] + diff --git a/authentication-service/pom.xml b/authentication-service/pom.xml new file mode 100644 index 0000000..bd0d8f8 --- /dev/null +++ b/authentication-service/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.grokonez + jwtauthentication + 0.0.1-SNAPSHOT + jwtauthentication + Demo project for Spring Boot + + + 10 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + + mysql + mysql-connector-java + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + io.jsonwebtoken + jjwt + 0.9.0 + + + + io.springfox + springfox-swagger2 + 2.6.1 + compile + + + + io.springfox + springfox-swagger-ui + 2.6.1 + compile + + + org.springframework.kafka + spring-kafka + + + org.springframework.kafka + spring-kafka-test + test + + + org.codehaus.jackson + jackson-xc + 1.9.11 + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + 2.0.2.RELEASE + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/JwtauthenticationApplication.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/JwtauthenticationApplication.java new file mode 100644 index 0000000..d304baa --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/JwtauthenticationApplication.java @@ -0,0 +1,16 @@ +package com.grokonez.jwtauthentication; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@EnableEurekaClient +@SpringBootApplication +public class JwtauthenticationApplication { + + public static void main(String[] args) { + SpringApplication.run(JwtauthenticationApplication.class, args); + } + +} + diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/config/KafkaConfiguration.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/config/KafkaConfiguration.java new file mode 100644 index 0000000..043373a --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/config/KafkaConfiguration.java @@ -0,0 +1,36 @@ +package com.grokonez.jwtauthentication.config; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +import java.util.HashMap; +import java.util.Map; + +@EnableKafka +@Configuration +public class KafkaConfiguration { + @Bean + public ConsumerFactory consumerFactory(){ + Map config = new HashMap<>(); + + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"localhost:9092"); + config.put(ConsumerConfig.GROUP_ID_CONFIG,"group_id"); + config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class); + + return new DefaultKafkaConsumerFactory<>(config); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory(){ + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/config/SwaggerConfig.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/config/SwaggerConfig.java new file mode 100644 index 0000000..19aba0e --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/config/SwaggerConfig.java @@ -0,0 +1,22 @@ +package com.grokonez.jwtauthentication.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import static springfox.documentation.builders.PathSelectors.regex; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket productApi() { + return new Docket(DocumentationType.SWAGGER_2) + .select().apis(RequestHandlerSelectors.basePackage("com.grokonez.jwtauthentication.controller")) + .paths(regex("/api/auth.*")) + .build(); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/AuthRestAPIs.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/AuthRestAPIs.java new file mode 100644 index 0000000..2f73ae9 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/AuthRestAPIs.java @@ -0,0 +1,116 @@ +package com.grokonez.jwtauthentication.controller; + +import java.util.HashSet; +import java.util.Set; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.*; + +import com.grokonez.jwtauthentication.message.request.LoginForm; +import com.grokonez.jwtauthentication.message.request.SignUpForm; +import com.grokonez.jwtauthentication.message.response.JwtResponse; +import com.grokonez.jwtauthentication.message.response.ResponseMessage; +import com.grokonez.jwtauthentication.model.Role; +import com.grokonez.jwtauthentication.model.RoleName; +import com.grokonez.jwtauthentication.model.User; +import com.grokonez.jwtauthentication.repository.RoleRepository; +import com.grokonez.jwtauthentication.repository.UserRepository; +import com.grokonez.jwtauthentication.security.jwt.JwtProvider; + +@CrossOrigin(origins = "*", maxAge = 3600) +@RestController +@RequestMapping("/api/auth") +public class AuthRestAPIs { + + @Autowired + AuthenticationManager authenticationManager; + + @Autowired + UserRepository userRepository; + + @Autowired + RoleRepository roleRepository; + + @Autowired + PasswordEncoder encoder; + + @Autowired + JwtProvider jwtProvider; + + @PostMapping("/signin") + public ResponseEntity authenticateUser(@Valid @RequestBody LoginForm loginRequest) { + + Authentication authentication = authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + String jwt = jwtProvider.generateJwtToken(authentication); + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + + return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUsername(), userDetails.getAuthorities())); + } + + @GetMapping("/hi") + public ResponseEntity testResponse(){ + return new ResponseEntity("hello", HttpStatus.OK); + } + + @PostMapping("/signup") + public ResponseEntity registerUser(@Valid @RequestBody SignUpForm signUpRequest) { + if (userRepository.existsByUsername(signUpRequest.getUsername())) { + return new ResponseEntity<>(new ResponseMessage("Fail -> Username is already taken!"), + HttpStatus.BAD_REQUEST); + } + + if (userRepository.existsByEmail(signUpRequest.getEmail())) { + return new ResponseEntity<>(new ResponseMessage("Fail -> Email is already in use!"), + HttpStatus.BAD_REQUEST); + } + + // Creating user's account + User user = new User(signUpRequest.getName(), signUpRequest.getUsername(), signUpRequest.getEmail(), + encoder.encode(signUpRequest.getPassword())); + +// Set strRoles = signUpRequest.getRole(); + Set strRoles = new HashSet(); + strRoles.add("user"); + Set roles = new HashSet<>(); + + strRoles.forEach(role -> { + switch (role) { + case "admin": + Role adminRole = roleRepository.findByName(RoleName.ROLE_ADMIN) + .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); + roles.add(adminRole); + + break; +// case "pm": +// Role pmRole = roleRepository.findByName(RoleName.ROLE_PM) +// .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); +// roles.add(pmRole); +// +// break; + default: + Role userRole = roleRepository.findByName(RoleName.ROLE_USER) + .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); + roles.add(userRole); + } + }); + + user.setRoles(roles); + userRepository.save(user); + + return new ResponseEntity<>(new ResponseMessage("User registered successfully!"), HttpStatus.OK); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/TestRestAPIs.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/TestRestAPIs.java new file mode 100644 index 0000000..9b75437 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/controller/TestRestAPIs.java @@ -0,0 +1,18 @@ +package com.grokonez.jwtauthentication.controller; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@CrossOrigin(origins = "*", maxAge = 3600) +@RestController +public class TestRestAPIs { + + @GetMapping("/api/test/user") + //@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") + public String userAccess() { + return ">>> User Contents!"; + } + +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/LoginForm.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/LoginForm.java new file mode 100644 index 0000000..169240a --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/LoginForm.java @@ -0,0 +1,30 @@ +package com.grokonez.jwtauthentication.message.request; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +public class LoginForm { + @NotBlank + @Size(min=3, max = 60) + private String username; + + @NotBlank + @Size(min = 6, max = 40) + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/SignUpForm.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/SignUpForm.java new file mode 100644 index 0000000..149d097 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/request/SignUpForm.java @@ -0,0 +1,66 @@ +package com.grokonez.jwtauthentication.message.request; + +import java.util.Set; + +import javax.validation.constraints.*; + +public class SignUpForm { + @NotBlank + @Size(min = 3, max = 50) + private String name; + + @NotBlank + @Size(min = 3, max = 50) + private String username; + + @NotBlank + @Size(max = 60) + @Email + private String email; + + private Set role; + + @NotBlank + @Size(min = 6, max = 40) + private String password; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Set getRole() { + return this.role; + } + + public void setRole(Set role) { + this.role = role; + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/JwtResponse.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/JwtResponse.java new file mode 100644 index 0000000..e3b1609 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/JwtResponse.java @@ -0,0 +1,46 @@ +package com.grokonez.jwtauthentication.message.response; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; + +public class JwtResponse { + private String token; + private String type = "Bearer"; + private String username; + private Collection authorities; + + public JwtResponse(String accessToken, String username, Collection authorities) { + this.token = accessToken; + this.username = username; + this.authorities = authorities; + } + + public String getAccessToken() { + return token; + } + + public void setAccessToken(String accessToken) { + this.token = accessToken; + } + + public String getTokenType() { + return type; + } + + public void setTokenType(String tokenType) { + this.type = tokenType; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Collection getAuthorities() { + return authorities; + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/ResponseMessage.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/ResponseMessage.java new file mode 100644 index 0000000..bd1b9db --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/message/response/ResponseMessage.java @@ -0,0 +1,17 @@ +package com.grokonez.jwtauthentication.message.response; + +public class ResponseMessage { + private String message; + + public ResponseMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/Role.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/Role.java new file mode 100644 index 0000000..2fe5cf2 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/Role.java @@ -0,0 +1,47 @@ +package com.grokonez.jwtauthentication.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.NaturalId; + +@Entity +@Table(name = "roles") +public class Role { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + @NaturalId + @Column(length = 60) + private RoleName name; + + public Role() {} + + public Role(RoleName name) { + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public RoleName getName() { + return name; + } + + public void setName(RoleName name) { + this.name = name; + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/RoleName.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/RoleName.java new file mode 100644 index 0000000..4b4a01e --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/RoleName.java @@ -0,0 +1,7 @@ +package com.grokonez.jwtauthentication.model; + +public enum RoleName { + ROLE_USER, +// ROLE_PM, + ROLE_ADMIN +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/User.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/User.java new file mode 100644 index 0000000..edc7d76 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/model/User.java @@ -0,0 +1,116 @@ +package com.grokonez.jwtauthentication.model; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import org.hibernate.annotations.NaturalId; + +@Entity +@Table(name = "users", uniqueConstraints = { + @UniqueConstraint(columnNames = { + "username" + }), + @UniqueConstraint(columnNames = { + "email" + }) +}) +public class User{ + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotBlank + @Size(min=3, max = 50) + private String name; + + @NotBlank + @Size(min=3, max = 50) + private String username; + + @NaturalId + @NotBlank + @Size(max = 50) + @Email + private String email; + + @NotBlank + @Size(min=6, max = 100) + private String password; + + @ManyToMany(fetch = FetchType.LAZY) + @JoinTable(name = "user_roles", + joinColumns = @JoinColumn(name = "user_id"), + inverseJoinColumns = @JoinColumn(name = "role_id")) + private Set roles = new HashSet<>(); + + public User() {} + + public User(String name, String username, String email, String password) { + this.name = name; + this.username = username; + this.email = email; + this.password = password; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/RoleRepository.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/RoleRepository.java new file mode 100644 index 0000000..1edfaa7 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/RoleRepository.java @@ -0,0 +1,14 @@ +package com.grokonez.jwtauthentication.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.grokonez.jwtauthentication.model.Role; +import com.grokonez.jwtauthentication.model.RoleName; + +@Repository +public interface RoleRepository extends JpaRepository { + Optional findByName(RoleName roleName); +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/UserRepository.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/UserRepository.java new file mode 100644 index 0000000..85a4fd6 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/repository/UserRepository.java @@ -0,0 +1,16 @@ +package com.grokonez.jwtauthentication.repository; + +import java.util.Optional; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import com.grokonez.jwtauthentication.model.User; + +@Repository +public interface UserRepository extends JpaRepository { + Optional findByUsername(String username); + Boolean existsByUsername(String username); + Boolean existsByEmail(String email); + +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/WebSecurityConfig.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/WebSecurityConfig.java new file mode 100644 index 0000000..bb56b13 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/WebSecurityConfig.java @@ -0,0 +1,68 @@ +package com.grokonez.jwtauthentication.security; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +import com.grokonez.jwtauthentication.security.jwt.JwtAuthEntryPoint; +import com.grokonez.jwtauthentication.security.jwt.JwtAuthTokenFilter; +import com.grokonez.jwtauthentication.security.services.UserDetailsServiceImpl; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity( + prePostEnabled = true +) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + UserDetailsServiceImpl userDetailsService; + + @Autowired + private JwtAuthEntryPoint unauthorizedHandler; + + @Bean + public JwtAuthTokenFilter authenticationJwtTokenFilter() { + return new JwtAuthTokenFilter(); + } + + @Override + public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + authenticationManagerBuilder + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().and().csrf().disable(). + authorizeRequests() + .antMatchers("/api/auth/**").permitAll() + .anyRequest().authenticated() + .and() + .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + + http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthEntryPoint.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthEntryPoint.java new file mode 100644 index 0000000..637c913 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthEntryPoint.java @@ -0,0 +1,29 @@ +package com.grokonez.jwtauthentication.security.jwt; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +@Component +public class JwtAuthEntryPoint implements AuthenticationEntryPoint { + + private static final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class); + + @Override + public void commence(HttpServletRequest request, + HttpServletResponse response, + AuthenticationException e) + throws IOException, ServletException { + + logger.error("Unauthorized error. Message - {}", e.getMessage()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized"); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthTokenFilter.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthTokenFilter.java new file mode 100644 index 0000000..917e312 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtAuthTokenFilter.java @@ -0,0 +1,63 @@ +package com.grokonez.jwtauthentication.security.jwt; + +import java.io.IOException; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; + +import com.grokonez.jwtauthentication.security.services.UserDetailsServiceImpl; + +public class JwtAuthTokenFilter extends OncePerRequestFilter { + + @Autowired + private JwtProvider tokenProvider; + + @Autowired + private UserDetailsServiceImpl userDetailsService; + + private static final Logger logger = LoggerFactory.getLogger(JwtAuthTokenFilter.class); + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + + String jwt = getJwt(request); + if (jwt != null && tokenProvider.validateJwtToken(jwt)) { + String username = tokenProvider.getUserNameFromJwtToken(jwt); + + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + userDetails, null, userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception e) { + logger.error("Can NOT set user authentication -> Message: {}", e); + } + + filterChain.doFilter(request, response); + } + + private String getJwt(HttpServletRequest request) { + String authHeader = request.getHeader("Authorization"); + + if (authHeader != null && authHeader.startsWith("Bearer ")) { + return authHeader.replace("Bearer ", ""); + } + + return null; + } +} diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtProvider.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtProvider.java new file mode 100644 index 0000000..6ccc4d9 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/jwt/JwtProvider.java @@ -0,0 +1,62 @@ +package com.grokonez.jwtauthentication.security.jwt; + +import io.jsonwebtoken.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; + +import com.grokonez.jwtauthentication.security.services.UserPrinciple; + +import java.util.Date; + +@Component +public class JwtProvider { + + private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class); + + @Value("${grokonez.app.jwtSecret}") + private String jwtSecret; + + @Value("${grokonez.app.jwtExpiration}") + private int jwtExpiration; + + public String generateJwtToken(Authentication authentication) { + + UserPrinciple userPrincipal = (UserPrinciple) authentication.getPrincipal(); + + return Jwts.builder() + .setSubject((userPrincipal.getUsername())) + .setIssuedAt(new Date()) + .setExpiration(new Date((new Date()).getTime() + jwtExpiration*1000)) + .signWith(SignatureAlgorithm.HS512, jwtSecret) + .compact(); + } + + public boolean validateJwtToken(String authToken) { + try { + Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); + return true; + } catch (SignatureException e) { + logger.error("Invalid JWT signature -> Message: {} ", e); + } catch (MalformedJwtException e) { + logger.error("Invalid JWT token -> Message: {}", e); + } catch (ExpiredJwtException e) { + logger.error("Expired JWT token -> Message: {}", e); + } catch (UnsupportedJwtException e) { + logger.error("Unsupported JWT token -> Message: {}", e); + } catch (IllegalArgumentException e) { + logger.error("JWT claims string is empty -> Message: {}", e); + } + + return false; + } + + public String getUserNameFromJwtToken(String token) { + return Jwts.parser() + .setSigningKey(jwtSecret) + .parseClaimsJws(token) + .getBody().getSubject(); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/KafkaListenerService.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/KafkaListenerService.java new file mode 100644 index 0000000..0345e68 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/KafkaListenerService.java @@ -0,0 +1,46 @@ +package com.grokonez.jwtauthentication.security.services; + +import com.grokonez.jwtauthentication.message.request.SignUpForm; +import com.grokonez.jwtauthentication.model.Role; +import com.grokonez.jwtauthentication.model.RoleName; +import com.grokonez.jwtauthentication.model.User; +import com.grokonez.jwtauthentication.repository.RoleRepository; +import com.grokonez.jwtauthentication.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import java.util.HashSet; +import java.util.Set; + +@Service +public class KafkaListenerService { + + @Autowired + UserRepository userRepository; + + @Autowired + RoleRepository roleRepository; + + @Autowired + PasswordEncoder encoder; + + @KafkaListener(topics = "Kafka_NewUser_Registration", groupId = "group_id") + public void consume(String message){ + System.out.println("Consumed msg : " + message); + String[] strMessage = message.split(","); + User user = new User(); + user.setName(strMessage[5].split(":")[1].replace("\"","")); + user.setEmail(strMessage[4].split(":")[1].replace("\"","")); + user.setUsername(strMessage[1].split(":")[1].replace("\"","")); +// user.setPassword(strMessage[2].split(":")[1].replace("\"","")); + user.setPassword(encoder.encode( (strMessage[2].split(":")[1].replace("\"","")) ) ); +// System.out.println(user.getName()); +// System.out.println(user.getEmail()); + System.out.println(user.getUsername()); + System.out.println(user.getPassword()); + userRepository.save(user); + System.out.println("New Message"); + System.out.println("Consumed msg : " + message); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserDetailsServiceImpl.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserDetailsServiceImpl.java new file mode 100644 index 0000000..4f0ca27 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserDetailsServiceImpl.java @@ -0,0 +1,27 @@ +package com.grokonez.jwtauthentication.security.services; + +import com.grokonez.jwtauthentication.model.User; +import com.grokonez.jwtauthentication.repository.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Autowired + UserRepository userRepository; + + @Override + @Transactional + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + + User user = userRepository.findByUsername(username).orElseThrow( + () -> new UsernameNotFoundException("User Not Found with -> username or email : " + username)); + + return UserPrinciple.build(user); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserPrinciple.java b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserPrinciple.java new file mode 100644 index 0000000..6918a41 --- /dev/null +++ b/authentication-service/src/main/java/com/grokonez/jwtauthentication/security/services/UserPrinciple.java @@ -0,0 +1,111 @@ +package com.grokonez.jwtauthentication.security.services; + +import com.grokonez.jwtauthentication.model.User; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class UserPrinciple implements UserDetails { + private static final long serialVersionUID = 1L; + + private Long id; + + private String name; + + private String username; + + private String email; + + @JsonIgnore + private String password; + + private Collection authorities; + + public UserPrinciple(Long id, String name, + String username, String email, String password, + Collection authorities) { + this.id = id; + this.name = name; + this.username = username; + this.email = email; + this.password = password; + this.authorities = authorities; + } + + public static UserPrinciple build(User user) { + List authorities = user.getRoles().stream().map(role -> + new SimpleGrantedAuthority(role.getName().name()) + ).collect(Collectors.toList()); + + return new UserPrinciple( + user.getId(), + user.getName(), + user.getUsername(), + user.getEmail(), + user.getPassword(), + authorities + ); + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + UserPrinciple user = (UserPrinciple) o; + return Objects.equals(id, user.id); + } +} \ No newline at end of file diff --git a/authentication-service/src/main/resources/application.properties b/authentication-service/src/main/resources/application.properties new file mode 100644 index 0000000..c9057c5 --- /dev/null +++ b/authentication-service/src/main/resources/application.properties @@ -0,0 +1,33 @@ +## Local properties + + +#server.port=8090 +#spring.jpa.generate-ddl=true +#spring.datasource.url= jdbc:mysql://localhost:3306/db1 +#spring.datasource.username=user +#spring.datasource.password= +#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +#spring.jpa.hibernate.ddl-auto=update +#spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect + + +# AWS properies + + +spring.datasource.url=jdbc:mysql://localhost:3306/dbauth +spring.datasource.username=root +spring.datasource.password=root123 +spring.jpa.generate-ddl=true +spring.jpa.hibernate.ddl-auto = update +server.port=8090 + + + + +# App Properties +grokonez.app.jwtSecret=jwtGrokonezSecretKey +grokonez.app.jwtExpiration=86400 + +spring.main.allow-bean-definition-overriding=true + + diff --git a/authentication-service/src/main/resources/application.yml b/authentication-service/src/main/resources/application.yml new file mode 100644 index 0000000..6993a0d --- /dev/null +++ b/authentication-service/src/main/resources/application.yml @@ -0,0 +1,21 @@ +eureka: #tells about the Eureka server details and its refresh time + instance: + leaseRenewalIntervalInSeconds: 1 + leaseExpirationDurationInSeconds: 2 + client: + serviceUrl: + defaultZone: http://localhost:8010/eureka/ + healthcheck: + enabled: true + lease: + duration: 5 +spring: + application: + name: login-service + #current service name to be used by the eureka server +management: + security: + enabled: false #disable the spring security on the management endpoints like /env, /refresh etc. +logging: + level: + com.example.howtodoinjava: DEBUG diff --git a/authentication-service/src/main/resources/roles.sql b/authentication-service/src/main/resources/roles.sql new file mode 100644 index 0000000..b871468 --- /dev/null +++ b/authentication-service/src/main/resources/roles.sql @@ -0,0 +1,3 @@ +INSERT INTO roles(name) VALUES('ROLE_USER'); +INSERT INTO roles(name) VALUES('ROLE_PM'); +INSERT INTO roles(name) VALUES('ROLE_ADMIN'); \ No newline at end of file diff --git a/authentication-service/src/test/java/com/grokonez/jwtauthentication/JwtauthenticationApplicationTests.java b/authentication-service/src/test/java/com/grokonez/jwtauthentication/JwtauthenticationApplicationTests.java new file mode 100644 index 0000000..b0f1f30 --- /dev/null +++ b/authentication-service/src/test/java/com/grokonez/jwtauthentication/JwtauthenticationApplicationTests.java @@ -0,0 +1,19 @@ +//package com.grokonez.jwtauthentication; +// +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.test.context.junit4.SpringRunner; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest +//public class JwtauthenticationApplicationTests { +// +// @Test +// public void contextLoads() { +// } +// +//} +// + + diff --git a/authentication-service/src/test/java/com/grokonez/jwtauthentication/controller/AuthRestAPIsTest.java b/authentication-service/src/test/java/com/grokonez/jwtauthentication/controller/AuthRestAPIsTest.java new file mode 100644 index 0000000..452d1a7 --- /dev/null +++ b/authentication-service/src/test/java/com/grokonez/jwtauthentication/controller/AuthRestAPIsTest.java @@ -0,0 +1,7 @@ +package com.grokonez.jwtauthentication.controller; + +import static org.junit.Assert.*; + +public class AuthRestAPIsTest { + +} \ No newline at end of file From 7a72747c1198b5ac2ccdbf977d7c959585c45770 Mon Sep 17 00:00:00 2001 From: Durgesh Date: Fri, 15 Feb 2019 10:31:20 +0530 Subject: [PATCH 14/38] Docker monitoring agent --- docker-agent | 1 + 1 file changed, 1 insertion(+) create mode 160000 docker-agent diff --git a/docker-agent b/docker-agent new file mode 160000 index 0000000..8dc9925 --- /dev/null +++ b/docker-agent @@ -0,0 +1 @@ +Subproject commit 8dc99254129437a5d4410e535691916f93228359 From 529a5f9a3f1587a17679dc98da6ce3e19e8ff63d Mon Sep 17 00:00:00 2001 From: Durgesh Date: Fri, 15 Feb 2019 10:35:05 +0530 Subject: [PATCH 15/38] Docker monitoring agent updated --- docker-agent-final/.gitignore | 25 +++++ docker-agent-final/README.md | 2 + docker-agent-final/pom.xml | 43 ++++++++ .../dockeragent/DockerAgentApplication.java | 14 +++ .../dockeragent/controller/Controller.java | 102 ++++++++++++++++++ .../dockeragent/domain/Metrics.java | 92 ++++++++++++++++ .../src/main/resources/application.properties | 1 + .../DockerAgentApplicationTests.java | 17 +++ 8 files changed, 296 insertions(+) create mode 100644 docker-agent-final/.gitignore create mode 100644 docker-agent-final/README.md create mode 100644 docker-agent-final/pom.xml create mode 100644 docker-agent-final/src/main/java/com/stackroute/dockeragent/DockerAgentApplication.java create mode 100644 docker-agent-final/src/main/java/com/stackroute/dockeragent/controller/Controller.java create mode 100644 docker-agent-final/src/main/java/com/stackroute/dockeragent/domain/Metrics.java create mode 100644 docker-agent-final/src/main/resources/application.properties create mode 100644 docker-agent-final/src/test/java/com/stackroute/dockeragent/DockerAgentApplicationTests.java diff --git a/docker-agent-final/.gitignore b/docker-agent-final/.gitignore new file mode 100644 index 0000000..c456c4a --- /dev/null +++ b/docker-agent-final/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/docker-agent-final/README.md b/docker-agent-final/README.md new file mode 100644 index 0000000..cabe442 --- /dev/null +++ b/docker-agent-final/README.md @@ -0,0 +1,2 @@ +# DockerMonitoringAgent +Agent to monitor Docker environment and collect their performance metrics data. diff --git a/docker-agent-final/pom.xml b/docker-agent-final/pom.xml new file mode 100644 index 0000000..96e0292 --- /dev/null +++ b/docker-agent-final/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.stackroute + docker-agent + 0.0.1-SNAPSHOT + docker-agent + Demo project for Spring Boot + + + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/docker-agent-final/src/main/java/com/stackroute/dockeragent/DockerAgentApplication.java b/docker-agent-final/src/main/java/com/stackroute/dockeragent/DockerAgentApplication.java new file mode 100644 index 0000000..b7849eb --- /dev/null +++ b/docker-agent-final/src/main/java/com/stackroute/dockeragent/DockerAgentApplication.java @@ -0,0 +1,14 @@ +package com.stackroute.dockeragent; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DockerAgentApplication { + + public static void main(String[] args) { + SpringApplication.run(DockerAgentApplication.class, args); + } + +} + diff --git a/docker-agent-final/src/main/java/com/stackroute/dockeragent/controller/Controller.java b/docker-agent-final/src/main/java/com/stackroute/dockeragent/controller/Controller.java new file mode 100644 index 0000000..84a4cb6 --- /dev/null +++ b/docker-agent-final/src/main/java/com/stackroute/dockeragent/controller/Controller.java @@ -0,0 +1,102 @@ +package com.stackroute.dockeragent.controller; + +import com.stackroute.dockeragent.domain.Metrics; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/docker") +public class Controller { + +// @Autowired +// private Metrics metrics; + +// @Autowired +// public Controller(Metrics metrics) { +// this.metrics = metrics; +// } + + @GetMapping("/stats") + public ResponseEntity getMetrics() throws IOException + { + + List commands = new ArrayList(); + commands.add("/bin/sh"); + commands.add("-c"); + commands.add("docker stats --no-stream"); // command + + // creating the process + ProcessBuilder pb = new ProcessBuilder(commands); + + // starting the process + Process process = pb.start(); + BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream())); +// System.out.println(stdInput.readLine()); + + String s = null; + String temp=""; + + int f=0; + List metricsList = new ArrayList<>(); + +// System.out.println("here is the string array"); + while ((s = stdInput.readLine()) != null) + { + + temp =temp+s+"\n"; + + if (f>=1) + { + Metrics metrics = new Metrics(); + + String[] str =s.trim().split("\\s+"); +// System.out.println("String[] : " + str); +// System.out.println("length ==="+str.length); + + // System.out.println(str[i]); + metrics.setContainerId(str[0]); + metrics.setContainerName(str[1]); + metrics.setCpu(str[2]); + metrics.setMem(str[6]); + metrics.setNetIO(str[7]+str[8]+str[9]); + metrics.setBlockIO(str[10]+str[10]+str[12]); + metrics.setpId(str[13]); + //System.out.println("Metrics : " + metrics.toString()); + metricsList.add(metrics); + } + f++; + + //System.out.println("jjjjjjjjjjjjjjjjjjjjjjjjjj"); + } +// System.out.println(metricsList.toString()); + for(Metrics model : metricsList) { + System.out.println(model); + } + return new ResponseEntity>(metricsList, HttpStatus.OK); + } + +} +// 01ba60de2082 +// elegant_mclaren +// 5.04% +// 1.985GiB +// / +// 15.54GiB +// 12.77% +// 807MB +// / +// 809MB +// 9.49MB +// / +// 1.32GB +// 307 \ No newline at end of file diff --git a/docker-agent-final/src/main/java/com/stackroute/dockeragent/domain/Metrics.java b/docker-agent-final/src/main/java/com/stackroute/dockeragent/domain/Metrics.java new file mode 100644 index 0000000..a7f478e --- /dev/null +++ b/docker-agent-final/src/main/java/com/stackroute/dockeragent/domain/Metrics.java @@ -0,0 +1,92 @@ +package com.stackroute.dockeragent.domain; + +import org.springframework.stereotype.Component; + +@Component +public class Metrics { + + + private String containerId; + private String containerName; + private String cpu; + private String mem; + private String netIO; + private String blockIO; + private String pId; + + + public String getContainerId() { + return containerId; + } + + public void setContainerId(String containerId) { + this.containerId = containerId; + } + + public String getContainerName() { + return containerName; + } + + public void setContainerName(String containerName) { + this.containerName = containerName; + } + + public String getNetIO() { + return netIO; + } + + public void setNetIO(String netIO) { + this.netIO = netIO; + } + + public String getBlockIO() { + return blockIO; + } + + public void setBlockIO(String blockIO) { + this.blockIO = blockIO; + } + + + + public Metrics() { + } + + public String getpId() { + return this.pId; + } + + public void setpId(String pId) { + this.pId = pId; + } + + public String getCpu() { + return this.cpu; + } + + public void setCpu(String cpu) { + this.cpu = cpu; + } + + public String getMem() { + return this.mem; + } + + public void setMem(String mem) { + this.mem = mem; + } + + + @Override + public String toString() { + return "Metrics{" + + "containerId='" + containerId + '\'' + + ", containerName='" + containerName + '\'' + + ", cpu='" + cpu + '\'' + + ", mem='" + mem + '\'' + + ", netIO='" + netIO + '\'' + + ", blockIO='" + blockIO + '\'' + + ", pId='" + pId + '\'' + + '}'; + } +} diff --git a/docker-agent-final/src/main/resources/application.properties b/docker-agent-final/src/main/resources/application.properties new file mode 100644 index 0000000..a97184f --- /dev/null +++ b/docker-agent-final/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port= 8020 \ No newline at end of file diff --git a/docker-agent-final/src/test/java/com/stackroute/dockeragent/DockerAgentApplicationTests.java b/docker-agent-final/src/test/java/com/stackroute/dockeragent/DockerAgentApplicationTests.java new file mode 100644 index 0000000..5ded228 --- /dev/null +++ b/docker-agent-final/src/test/java/com/stackroute/dockeragent/DockerAgentApplicationTests.java @@ -0,0 +1,17 @@ +package com.stackroute.dockeragent; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class DockerAgentApplicationTests { + + @Test + public void contextLoads() { + } + +} + From a1a13818e2488e113af76101b7c187dea0e46f2f Mon Sep 17 00:00:00 2001 From: anu Date: Fri, 15 Feb 2019 13:12:28 +0530 Subject: [PATCH 16/38] Added .war agent code --- war_agent/pom-with-docker-tests.xml | 386 ++++++++++++++++++ war_agent/pom.xml | 76 ++++ .../io/promagent/collectors/JmxCollector.java | 35 ++ .../java/io/promagent/hooks/HttpContext.java | 33 ++ .../java/io/promagent/hooks/JdbcHook.java | 128 ++++++ .../java/io/promagent/hooks/ServletHook.java | 105 +++++ .../test/java/io/promagent/it/SpringIT.java | 68 +++ .../test/java/io/promagent/it/WildflyIT.java | 67 +++ 8 files changed, 898 insertions(+) create mode 100644 war_agent/pom-with-docker-tests.xml create mode 100644 war_agent/pom.xml create mode 100644 war_agent/src/main/java/io/promagent/collectors/JmxCollector.java create mode 100644 war_agent/src/main/java/io/promagent/hooks/HttpContext.java create mode 100644 war_agent/src/main/java/io/promagent/hooks/JdbcHook.java create mode 100644 war_agent/src/main/java/io/promagent/hooks/ServletHook.java create mode 100644 war_agent/src/test/java/io/promagent/it/SpringIT.java create mode 100644 war_agent/src/test/java/io/promagent/it/WildflyIT.java diff --git a/war_agent/pom-with-docker-tests.xml b/war_agent/pom-with-docker-tests.xml new file mode 100644 index 0000000..99be21a --- /dev/null +++ b/war_agent/pom-with-docker-tests.xml @@ -0,0 +1,386 @@ + + 4.0.0 + + io.promagent + promagent-example + 1.0-SNAPSHOT + + promagent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.promagent + promagent-api + ${promagent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + promagent + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.1 + + + + + + io.promagent + promagent-maven-plugin + ${promagent.framework.version} + + + promagent + package + + build + + + + + + + + io.fabric8 + docker-maven-plugin + 0.27.2 + + true + + + + + ${project.artifactId}/oracle-jdk-8 + + ubuntu:17.10 + /opt + + apt-get update ; apt-get upgrade -y + apt-get install -y curl git neovim sudo + curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz + tar xfz jdk-8u192-linux-x64.tar.gz + curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz + tar xfz apache-maven-3.6.0-bin.tar.gz + + echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ + echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; + + adduser --disabled-password --gecos '' promagent + echo 'promagent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + + promagent + + /opt/jdk1.8.0_192 + /opt/apache-maven-3.6.0 + /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH + + + + + + + ${project.artifactId}/wildfly-kitchensink + + ${project.artifactId}/oracle-jdk-8 + /home/promagent + + curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz + tar xfz wildfly-10.1.0.Final.tar.gz + rm wildfly-10.1.0.Final.tar.gz + git clone https://github.com/wildfly/quickstart.git + cd quickstart; git checkout 10.x + cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package + mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments + + mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments + rm -rf ./quickstart ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/wildfly-kitchensink-promagent + + ${project.artifactId}/wildfly-kitchensink + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ + echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ + echo 'export JAVA_OPTS="' >> run.sh ; \ + echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ + echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ + echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.promagent.agent' >> run.sh ; \ + echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ + echo ' -javaagent:../promagent.jar=port=9300' >> run.sh ; \ + echo '"' >> run.sh ; \ + echo >> run.sh ; \ + echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/promagent + promagent:promagent:promagent + + + + target/promagent.jar + / + + + + + + + + + + ${wildfly.port}:8080 + ${promagent.port}:9300 + + + WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms + + + ./run.sh + + true + [Wildfly] + + + + + + + ${project.artifactId}/spring + + ${project.artifactId}/oracle-jdk-8 + /home/promagent + + git clone https://github.com/spring-guides/gs-accessing-data-rest.git + cd gs-accessing-data-rest/complete; mvn --batch-mode package + mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . + rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/spring-promagent + + ${project.artifactId}/spring + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'java -javaagent:promagent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/promagent + promagent:promagent:promagent + + + + target/promagent.jar + / + + + + + + + + + + ${spring.port}:8080 + ${promagent.port}:9300 + + + Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) + + + ./run.sh + + true + [Spring Boot] + + + + + + + + + start + pre-integration-test + + build + start + + + + stop + post-integration-test + + stop + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + none + + skip-docker-tests + + + + wildfly + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/wildfly-kitchensink, + ${project.artifactId}/wildfly-kitchensink-promagent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/WildflyIT.java + + + + + http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink + http://${docker.host.address}:${promagent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + spring + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/spring, + ${project.artifactId}/spring-promagent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/SpringIT.java + + + + + http://${docker.host.address}:${spring.port} + http://${docker.host.address}:${promagent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + diff --git a/war_agent/pom.xml b/war_agent/pom.xml new file mode 100644 index 0000000..0ef05e6 --- /dev/null +++ b/war_agent/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + io.promagent + promagent-example + 1.0-SNAPSHOT + + promagent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.promagent + promagent-api + ${promagent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + promagent + + + io.promagent + promagent-maven-plugin + ${promagent.framework.version} + + + promagent + package + + build + + + + + + + + diff --git a/war_agent/src/main/java/io/promagent/collectors/JmxCollector.java b/war_agent/src/main/java/io/promagent/collectors/JmxCollector.java new file mode 100644 index 0000000..fa31f86 --- /dev/null +++ b/war_agent/src/main/java/io/promagent/collectors/JmxCollector.java @@ -0,0 +1,35 @@ + +package io.promagent.collectors; + +import com.sun.management.OperatingSystemMXBean; +import io.prometheus.client.Collector; + +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) +// TODO -- This class is not loaded by default, see commented-out lines in io.promagent.internal.Promagent.premain() +// See JmxCollector in jmx_exporter +public class JmxCollector extends Collector implements Collector.Describable { + + @Override + public List collect() { + List result = new ArrayList<>(); + result.add(collectOperatingSystemMetrics()); + return Collections.unmodifiableList(result); + } + + @Override + public List describe() { + return new ArrayList<>(); + } + + private MetricFamilySamples collectOperatingSystemMetrics() { + OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); + return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); + } +} diff --git a/war_agent/src/main/java/io/promagent/hooks/HttpContext.java b/war_agent/src/main/java/io/promagent/hooks/HttpContext.java new file mode 100644 index 0000000..7d8dccc --- /dev/null +++ b/war_agent/src/main/java/io/promagent/hooks/HttpContext.java @@ -0,0 +1,33 @@ +package io.promagent.hooks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + + +/**Storing the http context in a thread to know which database request call was triggered. +*/ +class HttpContext { + + static class Key {} + + static final Key HTTP_METHOD = new Key<>(); + static final Key HTTP_PATH = new Key<>(); + + private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); + + static void put(Key key, T value) { + threadLocal.get().put(key, value); + } + + @SuppressWarnings("unchecked") + static Optional get(Key key) { + return Optional.ofNullable((T) threadLocal.get().get(key)); + } + + static void clear(Key... keys) { + for (Key key : keys) { + threadLocal.get().remove(key); + } + } +} diff --git a/war_agent/src/main/java/io/promagent/hooks/JdbcHook.java b/war_agent/src/main/java/io/promagent/hooks/JdbcHook.java new file mode 100644 index 0000000..675d449 --- /dev/null +++ b/war_agent/src/main/java/io/promagent/hooks/JdbcHook.java @@ -0,0 +1,128 @@ + +package io.promagent.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricDef; +import io.promagent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import java.util.concurrent.TimeUnit; + +import static io.promagent.hooks.HttpContext.HTTP_METHOD; +import static io.promagent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "java.sql.Statement", + "java.sql.Connection" +}) +public class JdbcHook { + + private final Counter sqlQueriesTotal; + private final Summary sqlQueriesDuration; + private long startTime = 0; + + public JdbcHook(MetricsStore metricsStore) { + + sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( + "sql_queries_total", + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "query") + .help("Total number of sql queries.") + .register(registry) + )); + + sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( + "sql_query_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "query") + .help("Duration for serving the sql queries in seconds.") + .register(registry) + )); + } + + private String stripValues(String query) { + // We want the structure of the query as labels, not the actual values. + // Therefore, we replace: + // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') + // with + // insert into Member (id, name, email, phone_number) values (...) + return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); + } + + // --- before + + @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void before(String sql) { + startTime = System.nanoTime(); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int autoGeneratedKeys) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int[] columnIndexes) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, String[] columnNames) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + before(sql); + } + + // --- after + + @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void after(String sql) throws Exception { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); + String path = HttpContext.get(HTTP_PATH).orElse("no http context"); + String query = stripValues(sql); + sqlQueriesTotal.labels(method, path, query).inc(); + sqlQueriesDuration.labels(method, path, query).observe(duration); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int autoGeneratedKeys) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int[] columnIndexes) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, String[] columnNames) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { + after(sql); + } +} diff --git a/war_agent/src/main/java/io/promagent/hooks/ServletHook.java b/war_agent/src/main/java/io/promagent/hooks/ServletHook.java new file mode 100644 index 0000000..e29e5ba --- /dev/null +++ b/war_agent/src/main/java/io/promagent/hooks/ServletHook.java @@ -0,0 +1,105 @@ + + +package io.promagent.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricDef; +import io.promagent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.concurrent.TimeUnit; + +import static io.promagent.hooks.HttpContext.HTTP_METHOD; +import static io.promagent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "javax.servlet.Servlet", + "javax.servlet.Filter" +}) +public class ServletHook { + + private final Counter httpRequestsTotal; + private final Summary httpRequestsDuration; + private long startTime = 0; + + public ServletHook(MetricsStore metricsStore) { + + httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( + "http_requests_total",https://github.com/asaini94/war_agent + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "status") + .help("Total number of http requests.") + .register(registry) + )); + + httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( + "http_request_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "status") + .help("Duration for serving the http requests in seconds.") + .register(registry) + )); + } + + private String stripPathParameters(String path) { + + // The URL path may include path parameters. + // For example, REST URLs for querying an item might look like this: + // + // /item/1 + // /item/2 + // /item/3 + // etc. + // + // /item/{id} + // + // This method replaces path parameters with placeholders. It is application specific and + // should be adapted depending on the actual paths in an application. + // For the demo, we just replace all numbers with {id}. + + return path + .replaceAll("/[0-9]+", "/{id}") + .replaceAll("/;jsessionid=\\w*", "") + .replaceAll("/$", "") + .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." + } + + @Before(method = {"service", "doFilter"}) + public void before(ServletRequest request, ServletResponse response) { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletRequest req = (HttpServletRequest) request; + HttpContext.put(HTTP_METHOD, req.getMethod()); + HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); + startTime = System.nanoTime(); + } + } + + // Return Werte und Exceptions als Parameter + @After(method = {"service", "doFilter"}) + public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletResponse resp = (HttpServletResponse) response; + try { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).get(); + String path = HttpContext.get(HTTP_PATH).get(); + httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); + httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); + } finally { + HttpContext.clear(HTTP_METHOD, HTTP_PATH); + } + } + } +} diff --git a/war_agent/src/test/java/io/promagent/it/SpringIT.java b/war_agent/src/test/java/io/promagent/it/SpringIT.java new file mode 100644 index 0000000..f3ceec9 --- /dev/null +++ b/war_agent/src/test/java/io/promagent/it/SpringIT.java @@ -0,0 +1,68 @@ + + +package io.promagent.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.junit.jupiter.api.Test; + +public class SpringIT { + + /** + * Run some HTTP queries against a Docker container from image promagent/spring-promagent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. + */ + @Test + public void testSpring() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); + + // Execute two POST requests + Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + + // Query Prometheus metrics from promagent + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .filter(m -> m.contains("status=\"201\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: + // query="insert into person (first_name, last_name, id)" + .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); + } + + private Request makePostRequest(String firstName, String lastName) { + return new Request.Builder() + .url(System.getProperty("deployment.url") + "/people") + .method("POST", RequestBody.create( + MediaType.parse("application/json"), + "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) + .build(); + } +} diff --git a/war_agent/src/test/java/io/promagent/it/WildflyIT.java b/war_agent/src/test/java/io/promagent/it/WildflyIT.java new file mode 100644 index 0000000..33b97a7 --- /dev/null +++ b/war_agent/src/test/java/io/promagent/it/WildflyIT.java @@ -0,0 +1,67 @@ + +package io.promagent.it; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class WildflyIT { + + /** + * Run some HTTP queries against a Docker container from image promagent/wildfly-kitchensink-promagent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. + */ + @Test + public void testWildfly() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); + + // Execute REST call + Response restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "1.0"); + + // Execute REST call again + restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "2.0"); + } + + private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { + + Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .filter(m -> m.contains("status=\"200\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); + } +} From 11599c55c98a7d7fb975622f5fe0b916709f8d38 Mon Sep 17 00:00:00 2001 From: anu Date: Fri, 15 Feb 2019 13:17:25 +0530 Subject: [PATCH 17/38] Java agent for jar files code --- java_jar_agent/pom-with-docker-tests.xml | 386 ++++++++++++++++++ java_jar_agent/pom.xml | 76 ++++ .../io/promagent/collectors/JmxCollector.java | 35 ++ .../java/io/promagent/hooks/HttpContext.java | 33 ++ .../java/io/promagent/hooks/JdbcHook.java | 128 ++++++ .../java/io/promagent/hooks/ServletHook.java | 105 +++++ .../test/java/io/promagent/it/SpringIT.java | 68 +++ .../test/java/io/promagent/it/WildflyIT.java | 67 +++ 8 files changed, 898 insertions(+) create mode 100644 java_jar_agent/pom-with-docker-tests.xml create mode 100644 java_jar_agent/pom.xml create mode 100644 java_jar_agent/src/main/java/io/promagent/collectors/JmxCollector.java create mode 100644 java_jar_agent/src/main/java/io/promagent/hooks/HttpContext.java create mode 100644 java_jar_agent/src/main/java/io/promagent/hooks/JdbcHook.java create mode 100644 java_jar_agent/src/main/java/io/promagent/hooks/ServletHook.java create mode 100644 java_jar_agent/src/test/java/io/promagent/it/SpringIT.java create mode 100644 java_jar_agent/src/test/java/io/promagent/it/WildflyIT.java diff --git a/java_jar_agent/pom-with-docker-tests.xml b/java_jar_agent/pom-with-docker-tests.xml new file mode 100644 index 0000000..99be21a --- /dev/null +++ b/java_jar_agent/pom-with-docker-tests.xml @@ -0,0 +1,386 @@ + + 4.0.0 + + io.promagent + promagent-example + 1.0-SNAPSHOT + + promagent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.promagent + promagent-api + ${promagent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + promagent + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.1 + + + + + + io.promagent + promagent-maven-plugin + ${promagent.framework.version} + + + promagent + package + + build + + + + + + + + io.fabric8 + docker-maven-plugin + 0.27.2 + + true + + + + + ${project.artifactId}/oracle-jdk-8 + + ubuntu:17.10 + /opt + + apt-get update ; apt-get upgrade -y + apt-get install -y curl git neovim sudo + curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz + tar xfz jdk-8u192-linux-x64.tar.gz + curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz + tar xfz apache-maven-3.6.0-bin.tar.gz + + echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ + echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; + + adduser --disabled-password --gecos '' promagent + echo 'promagent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + + promagent + + /opt/jdk1.8.0_192 + /opt/apache-maven-3.6.0 + /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH + + + + + + + ${project.artifactId}/wildfly-kitchensink + + ${project.artifactId}/oracle-jdk-8 + /home/promagent + + curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz + tar xfz wildfly-10.1.0.Final.tar.gz + rm wildfly-10.1.0.Final.tar.gz + git clone https://github.com/wildfly/quickstart.git + cd quickstart; git checkout 10.x + cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package + mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments + + mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments + rm -rf ./quickstart ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/wildfly-kitchensink-promagent + + ${project.artifactId}/wildfly-kitchensink + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ + echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ + echo 'export JAVA_OPTS="' >> run.sh ; \ + echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ + echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ + echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.promagent.agent' >> run.sh ; \ + echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ + echo ' -javaagent:../promagent.jar=port=9300' >> run.sh ; \ + echo '"' >> run.sh ; \ + echo >> run.sh ; \ + echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/promagent + promagent:promagent:promagent + + + + target/promagent.jar + / + + + + + + + + + + ${wildfly.port}:8080 + ${promagent.port}:9300 + + + WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms + + + ./run.sh + + true + [Wildfly] + + + + + + + ${project.artifactId}/spring + + ${project.artifactId}/oracle-jdk-8 + /home/promagent + + git clone https://github.com/spring-guides/gs-accessing-data-rest.git + cd gs-accessing-data-rest/complete; mvn --batch-mode package + mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . + rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/spring-promagent + + ${project.artifactId}/spring + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'java -javaagent:promagent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/promagent + promagent:promagent:promagent + + + + target/promagent.jar + / + + + + + + + + + + ${spring.port}:8080 + ${promagent.port}:9300 + + + Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) + + + ./run.sh + + true + [Spring Boot] + + + + + + + + + start + pre-integration-test + + build + start + + + + stop + post-integration-test + + stop + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + none + + skip-docker-tests + + + + wildfly + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/wildfly-kitchensink, + ${project.artifactId}/wildfly-kitchensink-promagent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/WildflyIT.java + + + + + http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink + http://${docker.host.address}:${promagent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + spring + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/spring, + ${project.artifactId}/spring-promagent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/SpringIT.java + + + + + http://${docker.host.address}:${spring.port} + http://${docker.host.address}:${promagent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + diff --git a/java_jar_agent/pom.xml b/java_jar_agent/pom.xml new file mode 100644 index 0000000..0ef05e6 --- /dev/null +++ b/java_jar_agent/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + io.promagent + promagent-example + 1.0-SNAPSHOT + + promagent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.promagent + promagent-api + ${promagent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + promagent + + + io.promagent + promagent-maven-plugin + ${promagent.framework.version} + + + promagent + package + + build + + + + + + + + diff --git a/java_jar_agent/src/main/java/io/promagent/collectors/JmxCollector.java b/java_jar_agent/src/main/java/io/promagent/collectors/JmxCollector.java new file mode 100644 index 0000000..fa31f86 --- /dev/null +++ b/java_jar_agent/src/main/java/io/promagent/collectors/JmxCollector.java @@ -0,0 +1,35 @@ + +package io.promagent.collectors; + +import com.sun.management.OperatingSystemMXBean; +import io.prometheus.client.Collector; + +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) +// TODO -- This class is not loaded by default, see commented-out lines in io.promagent.internal.Promagent.premain() +// See JmxCollector in jmx_exporter +public class JmxCollector extends Collector implements Collector.Describable { + + @Override + public List collect() { + List result = new ArrayList<>(); + result.add(collectOperatingSystemMetrics()); + return Collections.unmodifiableList(result); + } + + @Override + public List describe() { + return new ArrayList<>(); + } + + private MetricFamilySamples collectOperatingSystemMetrics() { + OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); + return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); + } +} diff --git a/java_jar_agent/src/main/java/io/promagent/hooks/HttpContext.java b/java_jar_agent/src/main/java/io/promagent/hooks/HttpContext.java new file mode 100644 index 0000000..7d8dccc --- /dev/null +++ b/java_jar_agent/src/main/java/io/promagent/hooks/HttpContext.java @@ -0,0 +1,33 @@ +package io.promagent.hooks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + + +/**Storing the http context in a thread to know which database request call was triggered. +*/ +class HttpContext { + + static class Key {} + + static final Key HTTP_METHOD = new Key<>(); + static final Key HTTP_PATH = new Key<>(); + + private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); + + static void put(Key key, T value) { + threadLocal.get().put(key, value); + } + + @SuppressWarnings("unchecked") + static Optional get(Key key) { + return Optional.ofNullable((T) threadLocal.get().get(key)); + } + + static void clear(Key... keys) { + for (Key key : keys) { + threadLocal.get().remove(key); + } + } +} diff --git a/java_jar_agent/src/main/java/io/promagent/hooks/JdbcHook.java b/java_jar_agent/src/main/java/io/promagent/hooks/JdbcHook.java new file mode 100644 index 0000000..675d449 --- /dev/null +++ b/java_jar_agent/src/main/java/io/promagent/hooks/JdbcHook.java @@ -0,0 +1,128 @@ + +package io.promagent.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricDef; +import io.promagent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import java.util.concurrent.TimeUnit; + +import static io.promagent.hooks.HttpContext.HTTP_METHOD; +import static io.promagent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "java.sql.Statement", + "java.sql.Connection" +}) +public class JdbcHook { + + private final Counter sqlQueriesTotal; + private final Summary sqlQueriesDuration; + private long startTime = 0; + + public JdbcHook(MetricsStore metricsStore) { + + sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( + "sql_queries_total", + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "query") + .help("Total number of sql queries.") + .register(registry) + )); + + sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( + "sql_query_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "query") + .help("Duration for serving the sql queries in seconds.") + .register(registry) + )); + } + + private String stripValues(String query) { + // We want the structure of the query as labels, not the actual values. + // Therefore, we replace: + // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') + // with + // insert into Member (id, name, email, phone_number) values (...) + return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); + } + + // --- before + + @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void before(String sql) { + startTime = System.nanoTime(); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int autoGeneratedKeys) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int[] columnIndexes) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, String[] columnNames) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + before(sql); + } + + // --- after + + @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void after(String sql) throws Exception { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); + String path = HttpContext.get(HTTP_PATH).orElse("no http context"); + String query = stripValues(sql); + sqlQueriesTotal.labels(method, path, query).inc(); + sqlQueriesDuration.labels(method, path, query).observe(duration); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int autoGeneratedKeys) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int[] columnIndexes) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, String[] columnNames) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { + after(sql); + } +} diff --git a/java_jar_agent/src/main/java/io/promagent/hooks/ServletHook.java b/java_jar_agent/src/main/java/io/promagent/hooks/ServletHook.java new file mode 100644 index 0000000..e29e5ba --- /dev/null +++ b/java_jar_agent/src/main/java/io/promagent/hooks/ServletHook.java @@ -0,0 +1,105 @@ + + +package io.promagent.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricDef; +import io.promagent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.concurrent.TimeUnit; + +import static io.promagent.hooks.HttpContext.HTTP_METHOD; +import static io.promagent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "javax.servlet.Servlet", + "javax.servlet.Filter" +}) +public class ServletHook { + + private final Counter httpRequestsTotal; + private final Summary httpRequestsDuration; + private long startTime = 0; + + public ServletHook(MetricsStore metricsStore) { + + httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( + "http_requests_total",https://github.com/asaini94/war_agent + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "status") + .help("Total number of http requests.") + .register(registry) + )); + + httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( + "http_request_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "status") + .help("Duration for serving the http requests in seconds.") + .register(registry) + )); + } + + private String stripPathParameters(String path) { + + // The URL path may include path parameters. + // For example, REST URLs for querying an item might look like this: + // + // /item/1 + // /item/2 + // /item/3 + // etc. + // + // /item/{id} + // + // This method replaces path parameters with placeholders. It is application specific and + // should be adapted depending on the actual paths in an application. + // For the demo, we just replace all numbers with {id}. + + return path + .replaceAll("/[0-9]+", "/{id}") + .replaceAll("/;jsessionid=\\w*", "") + .replaceAll("/$", "") + .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." + } + + @Before(method = {"service", "doFilter"}) + public void before(ServletRequest request, ServletResponse response) { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletRequest req = (HttpServletRequest) request; + HttpContext.put(HTTP_METHOD, req.getMethod()); + HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); + startTime = System.nanoTime(); + } + } + + // Return Werte und Exceptions als Parameter + @After(method = {"service", "doFilter"}) + public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletResponse resp = (HttpServletResponse) response; + try { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).get(); + String path = HttpContext.get(HTTP_PATH).get(); + httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); + httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); + } finally { + HttpContext.clear(HTTP_METHOD, HTTP_PATH); + } + } + } +} diff --git a/java_jar_agent/src/test/java/io/promagent/it/SpringIT.java b/java_jar_agent/src/test/java/io/promagent/it/SpringIT.java new file mode 100644 index 0000000..f3ceec9 --- /dev/null +++ b/java_jar_agent/src/test/java/io/promagent/it/SpringIT.java @@ -0,0 +1,68 @@ + + +package io.promagent.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.junit.jupiter.api.Test; + +public class SpringIT { + + /** + * Run some HTTP queries against a Docker container from image promagent/spring-promagent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. + */ + @Test + public void testSpring() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); + + // Execute two POST requests + Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + + // Query Prometheus metrics from promagent + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .filter(m -> m.contains("status=\"201\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: + // query="insert into person (first_name, last_name, id)" + .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); + } + + private Request makePostRequest(String firstName, String lastName) { + return new Request.Builder() + .url(System.getProperty("deployment.url") + "/people") + .method("POST", RequestBody.create( + MediaType.parse("application/json"), + "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) + .build(); + } +} diff --git a/java_jar_agent/src/test/java/io/promagent/it/WildflyIT.java b/java_jar_agent/src/test/java/io/promagent/it/WildflyIT.java new file mode 100644 index 0000000..33b97a7 --- /dev/null +++ b/java_jar_agent/src/test/java/io/promagent/it/WildflyIT.java @@ -0,0 +1,67 @@ + +package io.promagent.it; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class WildflyIT { + + /** + * Run some HTTP queries against a Docker container from image promagent/wildfly-kitchensink-promagent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. + */ + @Test + public void testWildfly() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); + + // Execute REST call + Response restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "1.0"); + + // Execute REST call again + restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "2.0"); + } + + private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { + + Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .filter(m -> m.contains("status=\"200\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); + } +} From d60c2f836aa7ebc9268a52ea73dfd49e138670ad Mon Sep 17 00:00:00 2001 From: sushmitha007 <42641604+sushmitha007@users.noreply.github.com> Date: Fri, 15 Feb 2019 14:23:04 +0530 Subject: [PATCH 18/38] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a367c6..2c1600c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ [![Build Status](https://travis-ci.org/stackroute/boeing-wave3-syscop.svg?branch=v1.0.0)](https://travis-ci.org/stackroute/boeing-wave3-syscop) +[![codecov](https://codecov.io/gh/stackroute/boeing-wave3-syscop/branch/v1.0.0/graph/badge.svg)](https://codecov.io/gh/stackroute/boeing-wave3-syscop) +![](https://img.shields.io/codecov/c/github/stackroute/boeing-wave3-syscop.svg?style=flat) -[![Coverage Status](https://coveralls.io/repos/github/stackroute/boeing-wave3-syscop/badge.svg?branch=v1.0.0)](https://coveralls.io/github/stackroute/boeing-wave3-syscop?branch=v1.0.0) +![](https://img.shields.io/snyk/vulnerabilities/github/stackroute/boeing-wave3-syscop.svg?style=popout) +![](https://img.shields.io/github/issues/stackroute/boeing-wave3-syscop.svg?style=popout) +![](https://img.shields.io/github/contributors/stackroute/boeing-wave3-syscop.svg?style=popout) +![](https://img.shields.io/github/last-commit/stackroute/boeing-wave3-syscop.svg?style=popout) + +![](https://img.shields.io/github/repo-size/stackroute/boeing-wave3-syscop.svg?style=popout) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) ****Running the System**** From 8501da18a14dfda7f195e219d998de22592995d2 Mon Sep 17 00:00:00 2001 From: Shri Date: Fri, 15 Feb 2019 14:32:10 +0530 Subject: [PATCH 19/38] Deleted eventbrite --- eventbrite-service/.gitignore | 25 --- eventbrite-service/Dockerfile | 6 - eventbrite-service/README.md | 0 eventbrite-service/pom.xml | 127 --------------- .../nextevent/NexteventApplication.java | 16 -- .../nextevent/config/SwaggerConfig.java | 22 --- .../nextevent/controller/EventController.java | 96 ------------ .../controller/GlobalExceptionHandler.java | 55 ------- .../exception/EventAlreadyExist.java | 9 -- .../nextevent/exception/EventNotFound.java | 11 -- .../stackroute/nextevent/model/Category.java | 29 ---- .../com/stackroute/nextevent/model/Event.java | 63 -------- .../nextevent/model/SubCategory.java | 17 -- .../com/stackroute/nextevent/model/Venu.java | 20 --- .../nextevent/repository/EventRepository.java | 13 -- .../nextevent/service/EventService.java | 17 -- .../nextevent/service/EventServiceImpl.java | 145 ------------------ .../src/main/resources/application.properties | 27 ---- .../nextevent/NexteventApplicationTests.java | 17 -- .../controller/EventControllerTest.java | 105 ------------- .../repository/EventRepositoryTestIt.java | 37 ----- pom.xml | 1 - 22 files changed, 858 deletions(-) delete mode 100644 eventbrite-service/.gitignore delete mode 100644 eventbrite-service/Dockerfile delete mode 100644 eventbrite-service/README.md delete mode 100644 eventbrite-service/pom.xml delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java delete mode 100644 eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java delete mode 100644 eventbrite-service/src/main/resources/application.properties delete mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java delete mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java delete mode 100644 eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java diff --git a/eventbrite-service/.gitignore b/eventbrite-service/.gitignore deleted file mode 100644 index 82eca33..0000000 --- a/eventbrite-service/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -/target/ -!.mvn/wrapper/maven-wrapper.jar - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/build/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ \ No newline at end of file diff --git a/eventbrite-service/Dockerfile b/eventbrite-service/Dockerfile deleted file mode 100644 index 9ead3ad..0000000 --- a/eventbrite-service/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM openjdk:11 - -ADD ./target/nextevent-0.0.1-SNAPSHOT.jar /event/src/nextevent-0.0.1-SNAPSHOT.jar -EXPOSE 8082 -WORKDIR event/src -ENTRYPOINT ["java","-jar","nextevent-0.0.1-SNAPSHOT.jar"] diff --git a/eventbrite-service/README.md b/eventbrite-service/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/eventbrite-service/pom.xml b/eventbrite-service/pom.xml deleted file mode 100644 index c4816bc..0000000 --- a/eventbrite-service/pom.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - 4.0.0 - - com.stackroute - syscop - 0.0.1-SNAPSHOT - ../pom.xml - - com.stackroute - eventbrite-service - 0.0.1-SNAPSHOT - eventbrite-service - nextEvent project in Spring Boot - - - UTF-8 - UTF-8 - 1.8 - ${java.version} - ${java.version} - - - - - - - - - - - - org.springframework.boot - spring-boot-starter-data-mongodb - - - io.springfox - springfox-swagger2 - 2.9.2 - - - org.springframework.cloud - spring-cloud-starter-netflix-zuul - - - io.springfox - springfox-swagger-ui - 2.9.2 - - - - org.springframework.cloud - spring-cloud-starter - - - org.springframework.cloud - spring-cloud-starter-netflix-eureka-client - - - org.springframework.boot - spring-boot-starter-web - - - - - - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.projectlombok - lombok-maven - 1.18.4.0 - pom - - - - org.projectlombok - lombok - 1.18.4 - provided - - - org.springframework.cloud - spring-cloud-commons - 2.0.1.RELEASE - - - - - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-compiler-plugin - - 11 - 11 - - - - - - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java deleted file mode 100644 index 36db246..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/NexteventApplication.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.stackroute.nextevent; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.netflix.eureka.EnableEurekaClient; - -@SpringBootApplication -@EnableEurekaClient -public class NexteventApplication { - - public static void main(String[] args) { - SpringApplication.run(NexteventApplication.class, args); - } - -} - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java deleted file mode 100644 index 4eeb351..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/config/SwaggerConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.stackroute.nextevent.config; - - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -@Configuration -@EnableSwagger2 -public class SwaggerConfig { - @Bean - public Docket customImplementation(){ - return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.basePackage("com.stackroute.nextevent")) - .build(); - } - } - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java deleted file mode 100644 index da9b5b2..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/EventController.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.stackroute.nextevent.controller; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import com.stackroute.nextevent.model.Event; -import com.stackroute.nextevent.service.EventService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("api/v1") -public class EventController { - private static Logger log = LoggerFactory.getLogger(EventController.class); - - private ResponseEntity responseEntity; - private EventService eventService; - - @Autowired - private EventController(final EventService eventService) { - this.eventService = eventService; - } - - @GetMapping("events") - public ResponseEntity getAllEvent() throws EventNotFound { - log.info("getAllEvevnt hit"); - responseEntity = new ResponseEntity(eventService.getAllEvent(), HttpStatus.OK); - return responseEntity; - } - - @PostMapping("events") - public ResponseEntity saveEvent(@RequestBody Event event) throws EventAlreadyExist{ - eventService.saveEvent(event); - responseEntity = new ResponseEntity(event, HttpStatus.CREATED); - - return responseEntity; - } - - @DeleteMapping("events/{id}") - public ResponseEntity deleteEvent(@PathVariable("id") String id) throws EventNotFound { - eventService.deleteEvent(id); - log.info("event mapping hit event deleted======"); - responseEntity = new ResponseEntity("Event deleted successfully", HttpStatus.OK); - return responseEntity; - } - - - @PutMapping("events/{id}") - public ResponseEntity updateEvent(@PathVariable("id") String id,@RequestBody Event event) throws EventNotFound { - log.info("upadte event hit"); - Event event1= eventService.updateEvent(event,id); - log.info("updated event+++++"+event1); - return responseEntity; - } - - @GetMapping("event/{id}") - public ResponseEntity getEventById(@PathVariable("id") String id) throws EventNotFound { - log.info("getEvent hit by Id"); - responseEntity = new ResponseEntity(eventService.getEventById(id), HttpStatus.OK); - return responseEntity; - } -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java deleted file mode 100644 index fcf5f58..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/controller/GlobalExceptionHandler.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.stackroute.nextevent.controller; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; - - -@ControllerAdvice -//@Log4j -public class GlobalExceptionHandler { - private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); - @ResponseStatus(value = HttpStatus.CONFLICT,reason = "Event Already Exists") - @ExceptionHandler(EventAlreadyExist.class) - public void handleEventAlreadyExists(EventAlreadyExist e) - { - log.error("Event alredy Exists"); - } - - @ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "Event Not Found") - @ExceptionHandler(EventNotFound.class) - public void handleEventNotFound() - { - log.error("Event Not Found"); - } - - @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR,reason = "Something went wrong ") - public void handleException(Exception e) - { - log.error("Try!!! After sometime"); - } -// @ResponseStatus(value= HttpStatus.BAD_REQUEST, reason="Not all mandatory fields are filled") -// @ExceptionHandler(EmptyFieldException.class) -// public void handleEmptyFieldException(EmptyFieldException e){ -// log.error("Not all mandatory fields are filled", e); -// } -// -// @ResponseStatus(value= HttpStatus.CONFLICT, reason="User already exists") -// @ExceptionHandler(UserAlreadyExistsException.class) -// public void handleUserAlreadyExistsException(UserAlreadyExistsException e){ -// log.error("User already exists", e); -// } -// -// @ResponseStatus(value= HttpStatus.NOT_FOUND, reason="User does not exist") -// @ExceptionHandler(UserDoesNotExistException.class) -// public void handleUserDoesNotExistException(UserDoesNotExistException e){ -// log.error("User does not exist", e); -// } - - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java deleted file mode 100644 index ca55e5c..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventAlreadyExist.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.stackroute.nextevent.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.CONFLICT, reason = "Event already exists on server") -public class EventAlreadyExist extends Exception { - public EventAlreadyExist(String message){super(message);} -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java deleted file mode 100644 index 34369e4..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/exception/EventNotFound.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.stackroute.nextevent.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Specified Event Not Found on server") -public class EventNotFound extends Exception { - - public EventNotFound(String message){super(message);} - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java deleted file mode 100644 index d2e3706..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Category.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.stackroute.nextevent.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.util.List; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Category { - private String categoryId; - private String name; - private List subCategory; - private int ageRestriction; - - @Override - public String toString() { - return "Category{" + - "categoryId='" + categoryId + '\'' + - ", name='" + name + '\'' + - ", subCategory=" + subCategory + - ", ageRestriction=" + ageRestriction + - '}'; - } -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java deleted file mode 100644 index 778a43d..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Event.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.stackroute.nextevent.model; - -import javax.validation.constraints.NotNull; -import java.util.Date; - - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; - -@Document -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Event { - - @Id - private String id; - - @NotNull - private String eventId; - - @NotNull - private String name; - - @NotNull - private String description; - - private Date created; - - //@NotNull - private Date start; - - //@NotNull - private Date end; - - private Venu venu; - - private Category category; - - @NotNull - private String imageUrl; - - @Override - public String toString() { - return "Event{" + - "id='" + id + '\'' + - ", eventId='" + eventId + '\'' + - ", name='" + name + '\'' + - ", description='" + description + '\'' + - ", created=" + created + - ", start=" + start + - ", end=" + end + - ", venu=" + venu + - ", category=" + category + - ", imageUrl='" + imageUrl + '\'' + - '}'; - } -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java deleted file mode 100644 index 59bca65..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/SubCategory.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.stackroute.nextevent.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class SubCategory { - String subcategoryId; - String name; - - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java deleted file mode 100644 index aa05a20..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/model/Venu.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.stackroute.nextevent.model; - - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -public class Venu { - String venuId; - String address; - String name; - int capacity; - - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java deleted file mode 100644 index 74e6132..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/repository/EventRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.stackroute.nextevent.repository; - -import com.stackroute.nextevent.model.Event; -import org.springframework.data.mongodb.repository.MongoRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface EventRepository extends MongoRepository { - - Event findByEventId(String id); - -} - diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java deleted file mode 100644 index 717f8db..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.stackroute.nextevent.service; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import com.stackroute.nextevent.model.Event; - -import java.util.List; - -public interface EventService { - - Event saveEvent(Event event) throws EventAlreadyExist; - boolean deleteEvent(String id) throws EventNotFound; - Event updateEvent(Event event,String id) throws EventNotFound; - Event getEventById(String id) throws EventNotFound; - List getAllEvent() throws EventNotFound; - -} diff --git a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java b/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java deleted file mode 100644 index ff536b8..0000000 --- a/eventbrite-service/src/main/java/com/stackroute/nextevent/service/EventServiceImpl.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.stackroute.nextevent.service; - -import com.stackroute.nextevent.exception.EventAlreadyExist; -import com.stackroute.nextevent.exception.EventNotFound; -import com.stackroute.nextevent.model.Event; -import com.stackroute.nextevent.repository.EventRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Optional; - -@Service -public class EventServiceImpl implements EventService { - - private static Logger log = LoggerFactory.getLogger(EventServiceImpl.class); - private EventRepository eventRepository; - // private Event event; - - @Autowired - EventServiceImpl(EventRepository eventRepository) - { - this.eventRepository = eventRepository; - } - - @Override - public Event saveEvent(Event event) throws EventAlreadyExist { - //return null; - Optional optional = eventRepository.findById(event.getId()); - if(optional.isPresent()) - throw new EventAlreadyExist("Event already exists"); - else - return eventRepository.save(event); - - } -// log.info("we are in SaveEvent"); -// System.out.println("++++++++++++++++++++++++++++"+eventRepository.findById(event.getId())); -// Optional optional = eventRepository.findById(event.getId()); -// if((optional.isPresent())){ -// throw new EventAlreadyExist("Event already exists"); -// } -// else { -// log.info("Event is+++++++++++" + event); -// return eventRepository.save(event); -// } - // } - - @Override - public boolean deleteEvent(String id) throws EventNotFound { - //return false; - // boolean status = false; - Optional optional = eventRepository.findById(id); - log.info("check event+++++++"+optional); - if(optional.isPresent()) { - eventRepository.deleteById(id); - System.out.println("Id is"+id); - // eventRepository.deleteById(id); - return true; - } - else{ - throw new EventNotFound("Event not Found"); - } - - //return status; -// Event event = eventRepository.getOne(id); -// if(event!=null) -// { -// eventRepository.delete(event); -// return true; -// } -// else { -// throw new EventNotFound("Event not found with this id"); -// } - - } - - @Override - public Event updateEvent(Event event, String id) throws EventNotFound { - //return null; - //return null; - Optional optional = eventRepository.findById(id); - if(optional.isPresent()){ - event = eventRepository.findById(id).get(); - event.setCreated(event.getCreated()); - event.setDescription(event.getDescription()); - event.setEnd(event.getEnd()); - event.setStart(event.getStart()); - event.setImageUrl(event.getImageUrl()); - event.setName(event.getName()); - event.setCategory(event.getCategory()); - log.info("event is======"+event); -// System.out.println(event); -// System.out.println("updated evet+++++++++++"+event); - log.info("updated event Saved"); - eventRepository.save(event); - // System.out.println("updated ++++++"+eventRepository.save(event)); - // muzixRepository.save(track); - //return updatedEvent; - - } - else - { - throw new EventNotFound("Event not found"); - } - return event; -// Event eventToBeUpdated = eventRepository.getOne(event.getId()); -// if(event!=null) -// { -// eventToBeUpdated.setCreated(event.getCreated()); -// eventToBeUpdated.setDescription(event.getDescription()); -// eventToBeUpdated.setEnd(event.getEnd()); -// eventToBeUpdated.setStart(event.getStart()); -// eventToBeUpdated.setImageUrl(event.getImageUrl()); -// eventToBeUpdated.setName(event.getName()); -// return eventRepository.save(eventToBeUpdated); -// -// } -// else { -// throw new EventNotFound("Event not found with this id"); -// } - } - - @Override - public Event getEventById(String id) throws EventNotFound { - //return null; - Optional optional = eventRepository.findById(id); - if(optional.isPresent()) { - log.info("inside if evntId found"); - return eventRepository.findById(id).get(); - } - else{ - throw new EventNotFound("Event not Found"); - } - - } - - @Override - public List getAllEvent() throws EventNotFound { - //return null; - return eventRepository.findAll(); - - } -} diff --git a/eventbrite-service/src/main/resources/application.properties b/eventbrite-service/src/main/resources/application.properties deleted file mode 100644 index 68822ed..0000000 --- a/eventbrite-service/src/main/resources/application.properties +++ /dev/null @@ -1,27 +0,0 @@ - -#spring.datasource.url=jdbc:mysql://localhost:3306/eventdata -#spring.datasource.username=root -#spring.datasource.password=123 -#spring.jpa.hibernate.ddl-auto=create-drop - - -server.port=8082 - - - -#sl4j -#logging.level.org.springframework.web=ERROR -#logging.level.com.stackroute=DEBUG -## Logging pattern for the console -#logging.pattern.console= "%d{yyyy-MM-dd HH:mm:ss} - %msg%n" -## Logging pattern for file -#logging.pattern.file= "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" -#logging.file=logs/application.log - -spring.application.name=event-service -eureka.client.serviceUrl.defaultZone=http://localhost:9091/eureka/ -eureka.client.register-with-eureka=true -eureka.client.fetch-registry=true - - - diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java deleted file mode 100644 index bbc9f3e..0000000 --- a/eventbrite-service/src/test/java/com/stackroute/nextevent/NexteventApplicationTests.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.stackroute.nextevent; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@SpringBootTest -public class NexteventApplicationTests { - - @Test - public void contextLoads() { - } - -} - diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java deleted file mode 100644 index 5ee8530..0000000 --- a/eventbrite-service/src/test/java/com/stackroute/nextevent/controller/EventControllerTest.java +++ /dev/null @@ -1,105 +0,0 @@ -//package com.stackroute.nextevent.controller; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.stackroute.nextevent.exception.EventAlreadyExist; -//import com.stackroute.nextevent.exception.EventNotFound; -//import com.stackroute.nextevent.model.Category; -//import com.stackroute.nextevent.model.Event; -//import com.stackroute.nextevent.model.SubCategory; -//import com.stackroute.nextevent.model.Venu; -//import com.stackroute.nextevent.service.EventService; -//import org.junit.After; -//import org.junit.Before; -//import org.junit.Test; -//import org.junit.runner.RunWith; -//import org.junit.runners.Parameterized; -//import org.mockito.InjectMocks; -//import org.mockito.MockitoAnnotations; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -//import org.springframework.boot.test.mock.mockito.MockBean; -//import org.springframework.http.MediaType; -//import org.springframework.test.context.junit4.SpringRunner; -//import org.springframework.test.context.web.WebAppConfiguration; -//import org.springframework.test.web.servlet.MockMvc; -//import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; -//import org.springframework.test.web.servlet.result.MockMvcResultHandlers; -//import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -//import org.springframework.test.web.servlet.setup.MockMvcBuilders; -// -//import java.text.DateFormat; -//import java.text.SimpleDateFormat; -//import java.time.LocalDate; -//import java.util.ArrayList; -//import java.util.Collection; -//import java.util.Date; -//import java.util.List; -// -//import static org.mockito.ArgumentMatchers.any; -//import static org.mockito.Mockito.when; -//import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -//import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -//import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -// -//@RunWith(SpringRunner.class) -//@WebMvcTest(EventController.class) -//public class EventControllerTest { -// @MockBean -// private EventService eventService; -// -// private MockMvc mockMvc; -// -// private Event event; -// -// @InjectMocks -// private EventController eventController; -// -// private List eventList; -// -// -// @Before -// public void setUp() throws Exception { -// eventList = new ArrayList<>(); -// MockitoAnnotations.initMocks(this); -// mockMvc = MockMvcBuilders.standaloneSetup(eventController).build(); -// Date dateFormat = new Date("1234/12/23 23:23:23"); -// Venu venu= new Venu("12","bangalore","bangalore",40); -// SubCategory subCategory= new SubCategory("1","music1"); -// List subCategoryList= new ArrayList<>(); -// subCategoryList.add(subCategory); -// Category category = new Category("12","music",subCategoryList,18); -// event = new Event( "123","123Efg","Event1","this is event1", dateFormat, dateFormat,dateFormat,venu,category,"imageurl"); -// eventList.add(event); -// } -// -// @After -// public void tearDown() throws Exception { -// -// } -// -// @Test -// public void testSaveEvent() throws Exception { -// when(eventService.saveEvent(any())).thenReturn(event); -// mockMvc.perform(MockMvcRequestBuilders.post("/api/v1/events") -// .contentType(MediaType.APPLICATION_JSON) -// .content(jsonToString(event))) -// .andExpect(MockMvcResultMatchers.status().isCreated()) -// .andDo(MockMvcResultHandlers.print()); -// } -// private static String jsonToString(final Object ob) throws JsonProcessingException { -// String result; -// -// try { -// ObjectMapper mapper = new ObjectMapper(); -// String jsonContent = mapper.writeValueAsString(ob); -// result = jsonContent; -// } catch(JsonProcessingException e) { -// result = "JSON processing error"; -// } -// -// return result; -// } -// -// -//} diff --git a/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java b/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java deleted file mode 100644 index fc641c0..0000000 --- a/eventbrite-service/src/test/java/com/stackroute/nextevent/repository/EventRepositoryTestIt.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.stackroute.nextevent.repository; - - -import com.stackroute.nextevent.model.Category; -import com.stackroute.nextevent.model.Event; -import com.stackroute.nextevent.model.SubCategory; -import com.stackroute.nextevent.model.Venu; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTest; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.junit4.SpringRunner; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Optional; - -import static org.junit.Assert.assertEquals; - -@RunWith(SpringRunner.class) -//@DataMongoTest -@SpringBootTest -public class EventRepositoryTestIt { - - @Test - public void findByDescription() throws Exception { - assertEquals("Teaspoon", "Teaspoon"); - } -} - diff --git a/pom.xml b/pom.xml index 523675c..cc8cafc 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,6 @@ eureka-server zuul-gatewayservice config-server - eventbrite-service From 6c126126341b638da6c27048556c4fc2eba9e60c Mon Sep 17 00:00:00 2001 From: Vaibhav AGochiya Date: Fri, 15 Feb 2019 14:38:11 +0530 Subject: [PATCH 20/38] Adding libraries for java agent --- .../{ => Promagent}/pom-with-docker-tests.xml | 0 java_jar_agent/{ => Promagent}/pom.xml | 0 .../io/promagent/collectors/JmxCollector.java | 0 .../java/io/promagent/hooks/HttpContext.java | 0 .../java/io/promagent/hooks/JdbcHook.java | 0 .../java/io/promagent/hooks/ServletHook.java | 0 .../test/java/io/promagent/it/SpringIT.java | 0 .../test/java/io/promagent/it/WildflyIT.java | 0 java_jar_agent/promagent-framework/pom.xml | 85 +++++ .../promagent-agent/pom.xml | 42 +++ .../io/promagent/agent/ClassLoaderCache.java | 82 +++++ .../java/io/promagent/agent/JarFiles.java | 141 ++++++++ .../agent/PerDeploymentClassLoader.java | 57 +++ .../java/io/promagent/agent/Promagent.java | 34 ++ .../java/io/promagent/agent/JarFilesTest.java | 59 +++ .../promagent-framework/promagent-api/pom.xml | 21 ++ .../java/io/promagent/annotations/After.java | 31 ++ .../java/io/promagent/annotations/Before.java | 31 ++ .../java/io/promagent/annotations/Hook.java | 45 +++ .../io/promagent/annotations/Returned.java | 45 +++ .../java/io/promagent/annotations/Thrown.java | 47 +++ .../io/promagent/hookcontext/MetricDef.java | 52 +++ .../promagent/hookcontext/MetricsStore.java | 54 +++ .../promagent-exporter/pom.xml | 31 ++ .../exporter/PromagentExporterServlet.java | 43 +++ .../promagent-internal/pom.xml | 69 ++++ .../io/promagent/internal/BuiltInServer.java | 69 ++++ .../java/io/promagent/internal/Delegator.java | 276 +++++++++++++++ .../io/promagent/internal/HookException.java | 26 ++ .../io/promagent/internal/HookInstance.java | 20 ++ .../io/promagent/internal/HookMetadata.java | 141 ++++++++ .../internal/HookMetadataParser.java | 335 ++++++++++++++++++ .../java/io/promagent/internal/Promagent.java | 155 ++++++++ .../promagent/internal/PromagentAdvice.java | 78 ++++ .../io/promagent/internal/jmx/Exporter.java | 41 +++ .../promagent/internal/jmx/ExporterMBean.java | 19 + .../io/promagent/internal/jmx/Metric.java | 47 +++ .../promagent/internal/jmx/MetricMBean.java | 28 ++ .../jmx/PromagentCollectorRegistry.java | 66 ++++ .../internal/HookMetadataParserTest.java | 114 ++++++ .../promagent/internal/HookMetadataTest.java | 48 +++ .../instrumentationtests/Instrumentor.java | 38 ++ .../instrumentationtests/LifecycleTest.java | 112 ++++++ .../MethodCallCounter.java | 66 ++++ .../ParameterTypesTest.java | 238 +++++++++++++ .../ReturnedAndThrownTest.java | 120 +++++++ .../instrumentationtests/StaticFinalTest.java | 66 ++++ .../internal/instrumentationtests/Util.java | 39 ++ .../instrumentationtests/classes/Fruit.java | 8 + .../classes/IParameterTypesExample.java | 39 ++ .../classes/IReturnedAndThrownExample.java | 23 ++ .../classes/ParameterTypesExample.java | 51 +++ .../classes/ReturnedAndThrownExample.java | 42 +++ .../classes/StaticFinalExample.java | 20 ++ .../hooks/LifecycleHookSkipFalse.java | 32 ++ .../hooks/LifecycleHookSkipTrue.java | 34 ++ .../hooks/OnlyAfterHook.java | 21 ++ .../hooks/OnlyBeforeHook.java | 21 ++ .../hooks/ParameterTypesHook.java | 167 +++++++++ .../hooks/ReturnedAndThrownHook.java | 84 +++++ .../hooks/StaticFinalTestHook.java | 33 ++ .../instrumentationtests/hooks/TwoHooks.java | 35 ++ .../promagent-loader/README.md | 20 ++ .../promagent-loader/pom.xml | 47 +++ .../io/promagent/loader/PromagentLoader.java | 87 +++++ .../promagent-maven-plugin/pom.xml | 73 ++++ .../promagent/plugin/AgentDependencies.java | 177 +++++++++ .../java/io/promagent/plugin/AgentJar.java | 135 +++++++ .../io/promagent/plugin/JarFileNames.java | 63 ++++ .../promagent/plugin/ManifestTransformer.java | 64 ++++ .../io/promagent/plugin/PromagentMojo.java | 62 ++++ 71 files changed, 4449 insertions(+) rename java_jar_agent/{ => Promagent}/pom-with-docker-tests.xml (100%) rename java_jar_agent/{ => Promagent}/pom.xml (100%) rename java_jar_agent/{ => Promagent}/src/main/java/io/promagent/collectors/JmxCollector.java (100%) rename java_jar_agent/{ => Promagent}/src/main/java/io/promagent/hooks/HttpContext.java (100%) rename java_jar_agent/{ => Promagent}/src/main/java/io/promagent/hooks/JdbcHook.java (100%) rename java_jar_agent/{ => Promagent}/src/main/java/io/promagent/hooks/ServletHook.java (100%) rename java_jar_agent/{ => Promagent}/src/test/java/io/promagent/it/SpringIT.java (100%) rename java_jar_agent/{ => Promagent}/src/test/java/io/promagent/it/WildflyIT.java (100%) create mode 100644 java_jar_agent/promagent-framework/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-agent/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java create mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/JarFiles.java create mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java create mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java create mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java create mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java create mode 100644 java_jar_agent/promagent-framework/promagent-exporter/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java create mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java create mode 100644 java_jar_agent/promagent-framework/promagent-loader/README.md create mode 100644 java_jar_agent/promagent-framework/promagent-loader/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java create mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml create mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java create mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java create mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/JarFileNames.java create mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/ManifestTransformer.java create mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/PromagentMojo.java diff --git a/java_jar_agent/pom-with-docker-tests.xml b/java_jar_agent/Promagent/pom-with-docker-tests.xml similarity index 100% rename from java_jar_agent/pom-with-docker-tests.xml rename to java_jar_agent/Promagent/pom-with-docker-tests.xml diff --git a/java_jar_agent/pom.xml b/java_jar_agent/Promagent/pom.xml similarity index 100% rename from java_jar_agent/pom.xml rename to java_jar_agent/Promagent/pom.xml diff --git a/java_jar_agent/src/main/java/io/promagent/collectors/JmxCollector.java b/java_jar_agent/Promagent/src/main/java/io/promagent/collectors/JmxCollector.java similarity index 100% rename from java_jar_agent/src/main/java/io/promagent/collectors/JmxCollector.java rename to java_jar_agent/Promagent/src/main/java/io/promagent/collectors/JmxCollector.java diff --git a/java_jar_agent/src/main/java/io/promagent/hooks/HttpContext.java b/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/HttpContext.java similarity index 100% rename from java_jar_agent/src/main/java/io/promagent/hooks/HttpContext.java rename to java_jar_agent/Promagent/src/main/java/io/promagent/hooks/HttpContext.java diff --git a/java_jar_agent/src/main/java/io/promagent/hooks/JdbcHook.java b/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/JdbcHook.java similarity index 100% rename from java_jar_agent/src/main/java/io/promagent/hooks/JdbcHook.java rename to java_jar_agent/Promagent/src/main/java/io/promagent/hooks/JdbcHook.java diff --git a/java_jar_agent/src/main/java/io/promagent/hooks/ServletHook.java b/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/ServletHook.java similarity index 100% rename from java_jar_agent/src/main/java/io/promagent/hooks/ServletHook.java rename to java_jar_agent/Promagent/src/main/java/io/promagent/hooks/ServletHook.java diff --git a/java_jar_agent/src/test/java/io/promagent/it/SpringIT.java b/java_jar_agent/Promagent/src/test/java/io/promagent/it/SpringIT.java similarity index 100% rename from java_jar_agent/src/test/java/io/promagent/it/SpringIT.java rename to java_jar_agent/Promagent/src/test/java/io/promagent/it/SpringIT.java diff --git a/java_jar_agent/src/test/java/io/promagent/it/WildflyIT.java b/java_jar_agent/Promagent/src/test/java/io/promagent/it/WildflyIT.java similarity index 100% rename from java_jar_agent/src/test/java/io/promagent/it/WildflyIT.java rename to java_jar_agent/Promagent/src/test/java/io/promagent/it/WildflyIT.java diff --git a/java_jar_agent/promagent-framework/pom.xml b/java_jar_agent/promagent-framework/pom.xml new file mode 100644 index 0000000..f9d4a3b --- /dev/null +++ b/java_jar_agent/promagent-framework/pom.xml @@ -0,0 +1,85 @@ + + 4.0.0 + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + pom + promagent framework + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 5.3.1 + + + + + + promagent-loader + + promagent-agent + promagent-api + promagent-internal + promagent-exporter + promagent-loader + promagent-maven-plugin + + + + + default + + true + + + promagent-agent + promagent-api + promagent-internal + promagent-exporter + promagent-maven-plugin + + + + + + + + + io.prometheus + simpleclient_common + 0.5.0 + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + test + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 2.22.1 + + + + + diff --git a/java_jar_agent/promagent-framework/promagent-agent/pom.xml b/java_jar_agent/promagent-framework/promagent-agent/pom.xml new file mode 100644 index 0000000..192ea65 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-agent/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + + promagent-agent + promagent-agent loaded from the system class loader + + jar + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + io.promagent.agent.Promagent + io.promagent.agent.Promagent + true + true + true + + + + + + + diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java new file mode 100644 index 0000000..6868f5c --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java @@ -0,0 +1,82 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.agent; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * ClassLoaderCache stores the class loaders used for loading the Promagent modules, + * i.e. promagent-hooks, promagent-annotations, promagent-internals, and their dependencies. + *

+ * For the Hooks (like ServletHook or JdbcHook) there is one class loader per deployment, + * because hook classes may reference classes from the deployment, + * e.g. as parameters to the before() and after() methods. + * All other modules and their dependencies are loaded through a shared class loader. + *

+ * When {@link #currentClassLoader()} is called for the first time within a class loader context, + * a new {@link PerDeploymentClassLoader} is created on the fly. + * Repeated calls in the same context yield the same {@link PerDeploymentClassLoader}. + */ +public class ClassLoaderCache { + + private static ClassLoaderCache instance; + + // TODO: The cache does not free class loaders when applications are undeployed. Maybe use WeakHashMap? + private final Map cache = new HashMap<>(); + private final URLClassLoader sharedClassLoader; // shared across multiple deployments + private final List perDeploymentJars; // one class loader for each deployment for these JARs + + private ClassLoaderCache(JarFiles jarFiles) { + sharedClassLoader = new URLClassLoader(pathsToURLs(jarFiles.getSharedJars())); + perDeploymentJars = jarFiles.getPerDeploymentJars(); + } + + public static synchronized ClassLoaderCache getInstance() { + if (instance == null) { + instance = new ClassLoaderCache(JarFiles.extract()); + } + return instance; + } + + public List getPerDeploymentJars() { + return perDeploymentJars; + } + + public synchronized ClassLoader currentClassLoader() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (! cache.containsKey(contextClassLoader)) { + cache.put(contextClassLoader, new PerDeploymentClassLoader(pathsToURLs(perDeploymentJars), sharedClassLoader, contextClassLoader)); + } + return cache.get(contextClassLoader); + } + + private static URL[] pathsToURLs(List paths) { + try { + URL[] result = new URL[paths.size()]; + for (int i=0; i perDeploymentJars; // classes loaded for each deployment + private final List sharedJars; // classes shared across multiple deployments + + private JarFiles(List perDeploymentJars, List sharedJars) { + this.perDeploymentJars = Collections.unmodifiableList(perDeploymentJars); + this.sharedJars = Collections.unmodifiableList(sharedJars); + } + + List getPerDeploymentJars() { + return perDeploymentJars; + } + + List getSharedJars() { + return sharedJars; + } + + /** + * Theoretically we could return a list of jar:/ URLs without extracting the JARs, + * but the URLClassLoader has a bug such that jar:/ URLs cannot be used. Therefore, we have + * to extract the JARs and return a list of file:/ URLs. + * See https://bugs.openjdk.java.net/browse/JDK-4735639 + */ + static JarFiles extract() { + List perDeploymentJars = new ArrayList<>(); + List sharedJars = new ArrayList<>(); + Path agentJar = findAgentJar(); + List extractedJars; + try { + Path tmpDir = Files.createTempDirectory("promagent-"); + tmpDir.toFile().deleteOnExit(); + extractedJars = unzip(agentJar, tmpDir, entry -> entry.getName().endsWith(".jar")); + } catch (IOException e) { + throw new RuntimeException("Failed to load promagent.jar: " + e.getMessage(), e); + } + for (Path jar : extractedJars) { + if (jar.getParent().getFileName().toString().equals("per-deployment-jars")) { + perDeploymentJars.add(jar); + } else { + sharedJars.add(jar); + } + } + return new JarFiles(perDeploymentJars, sharedJars); + } + + private static List unzip(Path jarFile, Path destDir, Predicate filter) throws IOException { + List result = new ArrayList<>(); + try (JarFile agentJar = new JarFile(jarFile.toFile())) { + Enumeration jarEntries = agentJar.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry jarEntry = jarEntries.nextElement(); + if (filter.test(jarEntry)) { + Path destFile = destDir.resolve(jarEntry.getName()); + if (!destFile.getParent().toFile().exists()) { + if (!destFile.getParent().toFile().mkdirs()) { + throw new IOException("Failed to make directory: " + destFile.getParent()); + } + } + Files.copy(agentJar.getInputStream(jarEntry), destFile); + result.add(destFile); + } + } + } + return result; + } + + private static Path findAgentJar() { + CodeSource cs = Promagent.class.getProtectionDomain().getCodeSource(); + if (cs != null) { + return findAgentJarFromCodeSource(cs); + } else { + // This happens if the Promagent class is loaded from the bootstrap class loader, + // i.e. in addition to the command line argument -javaagent:/path/to/promagent.jar, + // the argument -Xbootclasspath/p:/path/to/promagent.jar is used. + return findAgentJarFromCmdline(ManagementFactory.getRuntimeMXBean().getInputArguments()); + } + } + + private static Path findAgentJarFromCodeSource(CodeSource cs) { + try { + return Paths.get(cs.getLocation().toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException("Failed to load promagent.jar from " + cs.getLocation() + ": " + e.getMessage(), e); + } + } + + static Path findAgentJarFromCmdline(List cmdlineArgs) { + Pattern p = Pattern.compile("^-javaagent:(.*promagent([^/]*).jar)(=.*)?$"); + for (String arg : cmdlineArgs) { + Matcher m = p.matcher(arg); + if (m.matches()) { + return Paths.get(m.group(1)); + } + } + throw new RuntimeException("Failed to locate promagent.jar file."); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java new file mode 100644 index 0000000..bdce971 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java @@ -0,0 +1,57 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.agent; + +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Class loader for loading Hooks (like ServletHook or JdbcHook). + *

+ * There is one instance per deployment in an application server, because hook classes may + * reference classes from the deployment, e.g. as parameters to the before() and after() methods. + *

+ * However, loading shared classes like the Prometheus client library is delegated to the {@link #sharedClassLoader}, + * because the Prometheus metric registry should be accessible across all deployments within an application server. + */ +class PerDeploymentClassLoader extends URLClassLoader { + + private final URLClassLoader sharedClassLoader; // for loading the Prometheus client library + + PerDeploymentClassLoader(URL[] perDeploymentJars, URLClassLoader sharedClassLoader, ClassLoader parent) { + super(perDeploymentJars, parent); + this.sharedClassLoader = sharedClassLoader; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + return sharedClassLoader.loadClass(name); // The Prometheus client library should all have the same initiating loader across deployments. + } catch (ClassNotFoundException e) { + return super.loadClass(name); // Hooks should have different initiating loaders if the context loader differs. + } + } + + // Called by Byte buddy to load the PromagentAdvice. + @Override + public InputStream getResourceAsStream(String name) { + InputStream result = sharedClassLoader.getResourceAsStream(name); + if (result == null) { + result = super.getResourceAsStream(name); + } + return result; + } +} diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java new file mode 100644 index 0000000..321aa98 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java @@ -0,0 +1,34 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.agent; + +import java.lang.instrument.Instrumentation; + +/** + * We want as little dependencies as possible on the system class loader, + * so the actual agent is loaded in its own class loader and this agent delegates to it. + */ +public class Promagent { + + public static void premain(String agentArgs, Instrumentation inst) throws Exception { + ClassLoader agentClassLoader = ClassLoaderCache.getInstance().currentClassLoader(); + Class agentClass = agentClassLoader.loadClass("io.promagent.internal.Promagent"); + agentClass.getMethod("premain", String.class, Instrumentation.class).invoke(null, agentArgs, inst); + } + + public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { + premain(agentArgs, inst); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java b/java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java new file mode 100644 index 0000000..a10c2cd --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java @@ -0,0 +1,59 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.agent; + +import org.junit.jupiter.api.Test; + +import static io.promagent.agent.JarFiles.findAgentJarFromCmdline; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; + +class JarFilesTest { + + @Test + void testCmdlineParserWildfly() { + // The command line arguments are taken from the Wildfly application server example. + String[] cmdlineArgs = new String[]{ + "-D[Standalone]", + "-Xbootclasspath/p:/tmp/wildfly-10.1.0.Final/modules/system/layers/base/org/jboss/logmanager/main/jboss-logmanager-2.0.4.Final.jar", + "-Djboss.modules.system.pkgs=org.jboss.logmanager,io.promagent.agent", + "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", + "-javaagent:../promagent/promagent-dist/target/promagent.jar=port=9300", + "-Dorg.jboss.boot.log.file=/tmp/wildfly-10.1.0.Final/standalone/log/server.log", + "-Dlogging.configuration=file:/tmp/wildfly-10.1.0.Final/standalone/configuration/logging.properties" + }; + assertEquals("../promagent/promagent-dist/target/promagent.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); + } + + @Test + void testCmdlineParserVersioned() { + String[] cmdlineArgs = new String[] { + "-javaagent:promagent-1.0-SNAPSHOT.jar" + }; + assertEquals("promagent-1.0-SNAPSHOT.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); + } + + @Test() + void testCmdlineParserFailed() { + String[] cmdlineArgs = new String[] { + "-javaagent:/some/other/agent.jar", + "-jar", + "promagent.jar" + }; + Exception e = assertThrows(Exception.class, () -> findAgentJarFromCmdline(asList(cmdlineArgs))); + // The exception should contain some message indicating promagent.jar was not found. + assertTrue(e.getMessage().contains("promagent.jar")); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-api/pom.xml b/java_jar_agent/promagent-framework/promagent-api/pom.xml new file mode 100644 index 0000000..3651380 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + + promagent-api + promagent-api API for implementing hooks + + jar + + + + io.prometheus + simpleclient_common + + + diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java new file mode 100644 index 0000000..c32356a --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java @@ -0,0 +1,31 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The method annotated with @After is executed before exiting the instrumented method. + * The method annotated with @After must have exactly the same parameters as the instrumented method. + * The "method" parameter are the names of the instrumented methods. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface After { + String[] method(); +} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java new file mode 100644 index 0000000..9769082 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java @@ -0,0 +1,31 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The method annotated with @Before is executed before entering the instrumented method. + * The method annotated with @Before must have exactly the same parameters as the instrumented method. + * The "method" parameter are the names of the instrumented methods. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Before { + String[] method(); +} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java new file mode 100644 index 0000000..2c50a1b --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java @@ -0,0 +1,45 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The @Hook annotation indicates that the annotated class is a Hook. + * The parameter {@link Hook#instruments()} defines which classes or interfaces are instrumented by that Hook. + * The method annotations {@link Before} and {@link After} define which methods should be instrumented within that class or interface. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Hook { + + /** + * List of classes or interfaces to be instrumented. + */ + String[] instruments(); + + /** + * If true, nested calls are skipped. + * Nested means that the instrumented method calls another method that is instrumented with the same hook. + * In most cases, you would only be interested in the outer call, so the default is {@code true}. + * If set to {@code false}, nested calls will also be instrumented. + * For nested calls, the same Hook instance is re-used. + * For outer calls, a new Hook instance is created. + */ + boolean skipNestedCalls() default true; +} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java new file mode 100644 index 0000000..4223dd4 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java @@ -0,0 +1,45 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A parameter annotated with @Returned can be used in a Hook's @After method to capture the return value of the instrumented method. + * + * Example: In order to instrument the following method: + * + *

+ *     int sum(int a, int b) {...}
+ * 
+ * + * A Hook could use an @After method like this: + * + *
+ *    {@literal @}After(method = "sum")
+ *     void after(int a, int b, @Returned int sum) {...}
+ * 
+ * + * The parameter annotated with @Returned is optional, if the hook does not use the return value, the parameter can be omitted. + *

+ * If the instrumented method terminates exceptionally, the type's default value is assigned to the parameter, + * i.e. {@code 0} for numeric types and {@code null} for reference types. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Returned {} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java new file mode 100644 index 0000000..a37583c --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java @@ -0,0 +1,47 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A parameter annotated with @Thrown can be used in a Hook's @After method to capture an Exception thrown in an instrumented method. + * + * Example: In order to instrument the following method: + * + *

+ *     int div(int a, int b) {
+ *         return a / b;
+ *     }
+ * 
+ * + * A Hook could use an @After method like this: + * + *
+ *    {@literal @}After(method = "div")
+ *     void after(int a, int b, @Returned int result, @Thrown Throwable exception) {...}
+ * 
+ * + * In case everything goes well, the {@code result} will be the return value of {@code div()}, and {@code excption} will be {@code null}. + * If {@code b} is {@code 0}, the {@code result} will be {@code 0}, and {@code exception} will be an {@link ArithmeticException} (division by zero). + *

+ * The parameter annotated with @Thrown is optional, if the hook does not use the exception, the parameter can be omitted. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Thrown {} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java new file mode 100644 index 0000000..61a118e --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java @@ -0,0 +1,52 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.hookcontext; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; + +import java.util.function.BiFunction; + +/** + * See {@link #MetricDef(String, BiFunction)} + */ +public class MetricDef { + + private final String metricName; + private final BiFunction producer; + + /** + * See {@link MetricsStore}. + * @param metricName Name of the Prometheus metric, like "http_requests_total". + * @param producer Function to create a new metric and register it with the registry. + * This function will only be called once, subsequent calls to {@link MetricsStore#createOrGet(MetricDef)} + * will return the previously created metric with the specified name. + * The two parameters are the metricName as specified above, + * and the Prometheus registry where the new metric should be registered. + * For an example see JavaDoc for {@link MetricsStore}. + */ + public MetricDef(String metricName, BiFunction producer) { + this.metricName = metricName; + this.producer = producer; + } + + String getMetricName() { + return metricName; + } + + BiFunction getProducer() { + return producer; + } +} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java new file mode 100644 index 0000000..bcd9cb2 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java @@ -0,0 +1,54 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.hookcontext; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; + +/** + * Instead of creating Prometheus Metrics directly, Hooks should use the {@link MetricsStore} like this: + *

+ * Counter httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>(
+ *         "http_requests_total",
+ *         (name, registry) -> Counter.build()
+ *                 .name(name)
+ *                 .labelNames("method", "path", "status")
+ *                 .help("Total number of http requests.")
+ *                 .register(registry)
+ * ));
+ * 
+ * The Promgent framework will take care that each metric is created only once and re-used across re-deployments in an application server. + */ +public class MetricsStore { + + private final CollectorRegistry registry; + private final ConcurrentMap metrics = new ConcurrentHashMap<>(); + + public MetricsStore(CollectorRegistry registry) { + this.registry = registry; + } + + /** + * See {@link MetricsStore} and {@link MetricDef#MetricDef(String, BiFunction)}. + */ + @SuppressWarnings("unchecked") + public T createOrGet(MetricDef metricDef) { + return (T) metrics.computeIfAbsent(metricDef.getMetricName(), s -> metricDef.getProducer().apply(metricDef.getMetricName(), registry)); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-exporter/pom.xml b/java_jar_agent/promagent-framework/promagent-exporter/pom.xml new file mode 100644 index 0000000..e3850a7 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-exporter/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + + promagent-exporter + promagent.war + + + false + + + war + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + promagent + + diff --git a/java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java b/java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java new file mode 100644 index 0000000..86611d2 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java @@ -0,0 +1,43 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.exporter; + +import javax.management.ObjectName; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.management.ManagementFactory; + +/** + * This servlet simply calls the ExporterMBean via JMX and provides the result. + */ +@WebServlet("/") +public class PromagentExporterServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + String result = (String) ManagementFactory.getPlatformMBeanServer() + .getAttribute(new ObjectName("io.promagent:type=exporter"), "TextFormat"); + response.getWriter().println(result); + } catch (Exception e) { + response.setStatus(500); + response.getWriter().println("Failed to load Exporter MBean. Are you sure the Prometheus agent is running?"); + e.printStackTrace(); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/pom.xml b/java_jar_agent/promagent-framework/promagent-internal/pom.xml new file mode 100644 index 0000000..2734613 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + + promagent-internal + promagent-internal classes shared across deployments + + jar + + + + + ${project.groupId} + promagent-agent + ${project.version} + provided + + + + ${project.groupId} + promagent-api + ${project.version} + provided + + + + net.bytebuddy + byte-buddy + 1.9.4 + + + + commons-io + commons-io + 2.6 + + + + io.prometheus + simpleclient_common + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mockito + mockito-core + 2.23.0 + test + + + + javax.servlet + javax.servlet-api + 4.0.1 + test + + + + diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java new file mode 100644 index 0000000..8313f4e --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java @@ -0,0 +1,69 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.InetSocketAddress; +import java.util.Collections; + +/** + * Use the Java runtime's built-in {@link HttpServer} to export Prometheus metrics. + */ +class BuiltInServer { + + static void run(String host, String portString, CollectorRegistry registry) throws Exception { + try { + int port = Integer.parseInt(portString); + InetSocketAddress address = host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port); + HttpServer httpServer = HttpServer.create(address, 10); + httpServer.createContext("/", httpExchange -> { + if ("/metrics".equals(httpExchange.getRequestURI().getPath())) { + respondMetrics(registry, httpExchange); + } else { + respondRedirect(httpExchange); + } + }); + httpServer.start(); + } catch (NumberFormatException e) { + throw new RuntimeException("Failed to parse command line arguments: '" + portString + "' is not a valid port number."); + } + } + + private static void respondMetrics(CollectorRegistry registry, HttpExchange httpExchange) throws IOException { + StringWriter respBodyWriter = new StringWriter(); + respBodyWriter.write("# Metrics will become visible when they are updated for the first time.\n"); + TextFormat.write004(respBodyWriter, registry.metricFamilySamples()); + byte[] respBody = respBodyWriter.toString().getBytes("UTF-8"); + httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); + httpExchange.sendResponseHeaders(200, respBody.length); + httpExchange.getResponseBody().write(respBody); + httpExchange.getResponseBody().close(); + } + + private static void respondRedirect(HttpExchange httpExchange) throws IOException { + byte[] respBody = "Metrics are provided on the /metrics endpoint.".getBytes("UTF-8"); + httpExchange.getResponseHeaders().add("Location", "/metrics"); + httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); + httpExchange.sendResponseHeaders(302, respBody.length); + httpExchange.getResponseBody().write(respBody); + httpExchange.getResponseBody().close(); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java new file mode 100644 index 0000000..fcc3410 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java @@ -0,0 +1,276 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Returned; +import io.promagent.annotations.Thrown; +import io.promagent.hookcontext.MetricsStore; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Delegator is called from the Byte Buddy Advice, and calls the Hook's @Before and @After methods. + *

+ * TODO: This is called often, should be performance optimized, e.g. caching hook method handles, etc. + */ +public class Delegator { + + private static Delegator instance; // not thread-safe, but it is set only once in the agent's premain method. + + private final SortedSet hookMetadata; + private final MetricsStore metricsStore; + private final ClassLoaderCache classLoaderCache; + private final ThreadLocal, Object>> threadLocal; + + private Delegator(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { + this.hookMetadata = hookMetadata; + this.metricsStore = metricsStore; + this.classLoaderCache = classLoaderCache; + this.threadLocal = ThreadLocal.withInitial(HashMap::new); + } + + public static void init(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { + instance = new Delegator(hookMetadata, metricsStore, classLoaderCache); + } + + /** + * Should be called from the Advice's @OnMethodEnter method. Returns the list of Hooks to be passed on to after() + */ + public static List before(Class interceptedClass, Method interceptedMethod, Object[] args) { + return instance.doBefore(interceptedClass, interceptedMethod, args); + } + + private List doBefore(Class interceptedClass, Method interceptedMethod, Object[] args) { + List hookInstances = loadFromThreadLocalOrCreate(interceptedClass, interceptedMethod); + for (HookInstance hookInstance : hookInstances) { + invokeBefore(hookInstance.getInstance(), interceptedMethod, args); + } + return hookInstances; + } + + /** + * Should be called from the Advice's @OnMethodExit method. First parameter is the list of hooks returned by before() + */ + public static void after(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { + instance.doAfter(hookInstances, interceptedMethod, args, returned, thrown); + } + + private void doAfter(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { + if (hookInstances != null) { + for (HookInstance hookInstance : hookInstances) { + invokeAfter(hookInstance.getInstance(), interceptedMethod, args, returned, thrown); + if (!hookInstance.isRecursiveCall()) { + threadLocal.get().remove(hookInstance.getInstance().getClass()); + } + } + } + } + + private List loadFromThreadLocalOrCreate(Class interceptedClass, Method interceptedMethod) { + return hookMetadata.stream() + .filter(hook -> classOrInterfaceMatches(interceptedClass, hook)) + .filter(hook -> methodNameAndNumArgsMatch(interceptedMethod, hook)) + .map(hook -> loadHookClass(hook)) + .filter(hookClass -> argumentTypesMatch(hookClass, interceptedMethod)) + .filter(hookClass -> ! shouldBeSkipped(hookClass)) + .map(hookClass -> loadFromTheadLocalOrCreate(hookClass)) + .collect(Collectors.toList()); + } + + private static boolean classOrInterfaceMatches(Class classToBeInstrumented, HookMetadata hook) { + Set classesAndInterfaces = getAllSuperClassesAndInterfaces(classToBeInstrumented); + return hook.getInstruments().stream().anyMatch(classesAndInterfaces::contains); + } + + private static Set getAllSuperClassesAndInterfaces(Class clazz) { + Set result = new HashSet<>(); + addAllSuperClassesAndInterfaces(clazz, result); + return result; + } + + private static void addAllSuperClassesAndInterfaces(Class clazz, Set result) { + if (clazz == null) { + return; + } + if (result.contains(clazz.getName())) { + return; + } + result.add(clazz.getName()); + for (Class ifc : clazz.getInterfaces()) { + addAllSuperClassesAndInterfaces(ifc, result); + } + addAllSuperClassesAndInterfaces(clazz.getSuperclass(), result); + } + + private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata hook) { + return hook.getMethods().stream() + .anyMatch(instrumentedMethod -> methodNameAndNumArgsMatch(interceptedMethod, instrumentedMethod)); + } + + private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata.MethodSignature instrumentedMethod) { + if (!interceptedMethod.getName().equals(instrumentedMethod.getMethodName())) { + return false; + } + if (interceptedMethod.getParameterCount() != instrumentedMethod.getParameterTypes().size()) { + return false; + } + return true; + } + + private Class loadHookClass(HookMetadata hook) { + try { + return classLoaderCache.currentClassLoader().loadClass(hook.getHookClassName()); + } catch (ClassNotFoundException e) { + throw new HookException("Failed to load Hook class " + hook.getHookClassName() + ": " + e.getMessage(), e); + } + } + + private static boolean argumentTypesMatch(Class hookClass, Method interceptedMethod) { + List before = findHookMethods(Before.class, hookClass, interceptedMethod); + List after = findHookMethods(After.class, hookClass, interceptedMethod); + return ! (before.isEmpty() && after.isEmpty()); + } + + private static List findHookMethods(Class annotation, Class hookClass, Method interceptedMethod) throws HookException { + return Stream.of(hookClass.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(annotation)) + .filter(method -> getMethodNames(method.getAnnotation(annotation)).contains(interceptedMethod.getName())) + .filter(method -> parameterTypesMatch(method, interceptedMethod)) + .collect(Collectors.toList()); + } + + private static List getMethodNames(Annotation annotation) throws HookException { + if (Before.class.isAssignableFrom(annotation.getClass())) { + return Arrays.asList(((Before) annotation).method()); + } else if (After.class.isAssignableFrom(annotation.getClass())) { + return Arrays.asList(((After) annotation).method()); + } else { + throw new HookException("Unsupported Annotation: @" + annotation.getClass().getSimpleName() + "."); + } + } + + // TODO: We could extend this to find the "closest" match, like in Java method calls. + private static boolean parameterTypesMatch(Method hookMethod, Method interceptedMethod) { + List> hookParameterTypes = stripReturnedAndThrown(hookMethod); + if (hookParameterTypes.size() != interceptedMethod.getParameterCount()) { + return false; + } + for (int i = 0; i < hookParameterTypes.size(); i++) { + Class hookParam = hookParameterTypes.get(i); + Class interceptedParam = interceptedMethod.getParameterTypes()[i]; + if (!hookParam.equals(interceptedParam)) { + return false; + } + } + return true; + } + + private static List> stripReturnedAndThrown(Method hookMethod) { + Class[] allTypes = hookMethod.getParameterTypes(); + Annotation[][] annotations = hookMethod.getParameterAnnotations(); + if (allTypes.length != annotations.length) { + throw new HookException("Method.getParameterAnnotations() returned an unexpected value. This is a bug in promagent."); + } + List> result = new ArrayList<>(); + for (int i=0; i Returned.class.equals(a) || Thrown.class.equals(a))) { + result.add(allTypes[i]); + } + } + return result; + } + + private boolean shouldBeSkipped(Class hookClass) { + return hookClass.getAnnotation(io.promagent.annotations.Hook.class).skipNestedCalls() + && threadLocal.get().containsKey(hookClass); + } + + private HookInstance loadFromTheadLocalOrCreate(Class hookClass) { + Object existingHookInstance = threadLocal.get().get(hookClass); + if (existingHookInstance != null) { + return new HookInstance(existingHookInstance, true); + } else { + String errMsg = "Failed to create new instance of hook " + hookClass.getSimpleName() + ": "; + try { + Object newHookInstance = hookClass.getConstructor(MetricsStore.class).newInstance(metricsStore); + threadLocal.get().put(hookClass, newHookInstance); + return new HookInstance(newHookInstance, false); + } catch (NoSuchMethodException e) { + throw new HookException(errMsg + "Hook classes must have a public constructor with a single parameter of type " + MetricsStore.class.getSimpleName(), e); + } catch (Exception e) { + throw new HookException(errMsg + e.getMessage(), e); + } + } + } + + /** + * Invoke the matching Hook methods annotated with @Before + */ + private static void invokeBefore(Object hookInstance, Method interceptedMethod, Object[] args) throws HookException { + invoke(Before.class, hookInstance, interceptedMethod, args, null, null); + } + + /** + * Invoke the matching Hook methods annotated with @After + */ + private static void invokeAfter(Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { + invoke(After.class, hookInstance, interceptedMethod, args, returned, thrown); + } + + private static void invoke(Class annotation, Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { + if (args.length != interceptedMethod.getParameterCount()) { + throw new IllegalArgumentException("Number of provided arguments is " + args.length + ", but interceptedMethod expects " + interceptedMethod.getParameterCount() + " argument(s)."); + } + for (Method method : findHookMethods(annotation, hookInstance.getClass(), interceptedMethod)) { + try { + if (!method.isAccessible()) { + method.setAccessible(true); + } + method.invoke(hookInstance, addReturnedAndThrownArgs(method, args, returned, thrown)); + } catch (Exception e) { + throw new HookException("Failed to call " + method.getName() + "() on " + hookInstance.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + } + + private static Object[] addReturnedAndThrownArgs(Method hookMethod, Object[] args, Object returned, Throwable thrown) { + Annotation[][] annotations = hookMethod.getParameterAnnotations(); + List result = new ArrayList<>(); + int arg = 0; + for (Annotation[] annotation : annotations) { + if (Arrays.stream(annotation) + .map(Annotation::annotationType) + .anyMatch(Returned.class::equals)) { + result.add(returned); + } else if (Arrays.stream(annotation) + .map(Annotation::annotationType) + .anyMatch(Thrown.class::equals)) { + result.add(thrown); + } else { + result.add(args[arg++]); + } + } + return result.toArray(); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java new file mode 100644 index 0000000..d8fbcaa --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java @@ -0,0 +1,26 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +public class HookException extends RuntimeException { + + public HookException(String message) { + super(message); + } + + public HookException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java new file mode 100644 index 0000000..7af2242 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java @@ -0,0 +1,20 @@ +package io.promagent.internal; + +public class HookInstance { + + private final Object instance; + private final boolean isRecursiveCall; // true if we have taken an existing instance from the ThreadLocal + + public HookInstance(Object instance, boolean isRecursiveCall) { + this.instance = instance; + this.isRecursiveCall = isRecursiveCall; + } + + public Object getInstance() { + return instance; + } + + public boolean isRecursiveCall() { + return isRecursiveCall; + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java new file mode 100644 index 0000000..9c3f877 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java @@ -0,0 +1,141 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Metadata for a Hook class as parsed from the @Hook, @Before, and @After annotations. + */ +public class HookMetadata implements Comparable { + + private final String hookClassName; + private final SortedSet instruments; + private final SortedSet methods; + + public static class MethodSignature implements Comparable { + + private final String methodName; + private final List parameterTypes; + + public MethodSignature(String methodName, List parameterTypes) { + this.methodName = methodName; + this.parameterTypes = Collections.unmodifiableList(new ArrayList<>(parameterTypes)); + } + + public String getMethodName() { + return methodName; + } + + public List getParameterTypes() { + return parameterTypes; + } + + @Override + public String toString() { + return methodName + "(" + String.join(", ", parameterTypes) + ")"; + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && compareTo((MethodSignature) o) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(toString()); + } + + @Override + public int compareTo(MethodSignature o) { + return Comparator + .comparing(MethodSignature::getMethodName) + .thenComparing(MethodSignature::getParameterTypes, new LexicographicalComparator<>()) + .compare(this, o); + } + } + + public HookMetadata(String hookClassName, Collection instruments, Collection methods) { + this.hookClassName = hookClassName; + this.instruments = Collections.unmodifiableSortedSet(new TreeSet<>(instruments)); + this.methods = Collections.unmodifiableSortedSet(new TreeSet<>(methods)); + } + + public String getHookClassName() { + return hookClassName; + } + + public SortedSet getInstruments() { + return instruments; + } + + public SortedSet getMethods() { + return methods; + } + + @Override + public String toString() { // TODO: instruments is a Set + String delimiter = System.lineSeparator() + " * "; + return hookClassName + " instruments [" + String.join(", ", instruments) + "]:" + delimiter + String.join(delimiter, strings(methods)); + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && compareTo((HookMetadata) o) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(toString()); + } + + @Override + public int compareTo(HookMetadata o) { + return Comparator + .comparing(HookMetadata::getHookClassName) + .thenComparing(HookMetadata::getInstruments, new LexicographicalComparator<>()) + .thenComparing(HookMetadata::getMethods, new LexicographicalComparator<>()) + .compare(this, o); + } + + private List strings(Collection list) { + return list.stream().map(Object::toString).collect(Collectors.toList()); + } + + /** + * Compare SortedSet instances or List instances in lexicographical order. + */ + static class LexicographicalComparator, T extends Iterable> implements Comparator { + @Override + public int compare(T list1, T list2) { + Iterator iterator1 = list1.iterator(); + Iterator iterator2 = list2.iterator(); + while (iterator1.hasNext() && iterator2.hasNext()) { + int result = iterator1.next().compareTo(iterator2.next()); + if (result != 0) { + return result; + } + } + if (iterator1.hasNext()) { + return 1; + } + if (iterator2.hasNext()) { + return -1; + } + return 0; + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java new file mode 100644 index 0000000..c69043a --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java @@ -0,0 +1,335 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Returned; +import io.promagent.annotations.Thrown; +import io.promagent.internal.HookMetadata.MethodSignature; +import net.bytebuddy.jar.asm.*; +import org.apache.commons.io.IOUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Inspect all hook classes in a JAR file and load the metadata from the @Hook, @Before, and @After annotations. + *

+ * The tricky part is that we cannot create an instance of the hook classes, because they might reference + * classes that are not available in the agent's premain phase. We need to parse the metadata without instantiating + * the hook classes. + */ +public class HookMetadataParser { + + private final SortedSet hookJars; + + public HookMetadataParser(Collection hookJars) { + this.hookJars = Collections.unmodifiableSortedSet(new TreeSet<>(hookJars)); + } + + /** + * Get the {@link HookMetadata} for the hook classes without loading the hook classes. + *

+ * We unsuccessfully tried two alternative implementations before we ended up with the current one. + * The first attempt was to use reflection like this: + *

+     *     Class hookClass = HookConfig.class.getClassLoader().loadClass(hookClassName);
+     *     for (Method m : hookClass.getMethods()) {
+     *         // ... get methodConfig
+     *     }
+     * 
+ * However, this throws a ClassNotFoundException in hookClass.getMethods(), because + * a hook method may have parameters like ServletRequest that are not available during agent startup. + *

+ * The second attempt was to analyze the hook using Byte buddy like this: + *

+     *     TypeDescription typeDesc = typePool.describe(hookClassName).resolve();
+     *     for (MethodDescription methodDesc : typeDesc.getDeclaredMethods()) {
+     *         TypeList params = methodDesc.getParameters().asTypeList().asErasures();
+     *         // ... get methodConfig
+     *     }
+     * 
+ * However, this throws a ClassNotFoundException in methodDesc.getParameters().asTypeList(). + * We did not find any way in the Byte buddy API to access a method's parameter types without loading these types. + *

+ * The current implementation uses ASM directly to analyze the hook class's byte code. + */ + SortedSet parse() throws IOException, ClassNotFoundException { + return parse(className -> true); // Parse all classes found in the JAR files. + } + + /** + * See {@link #parse()}. + * + * The classNameFilter is used to parse only specific classes from the JAR files. + */ + public SortedSet parse(Predicate classNameFilter) throws IOException, ClassNotFoundException { + SortedSet result = new TreeSet<>(); + for (String className : listAllJavaClasses(hookJars, classNameFilter)) { + byte[] binaryRepresentation = readBinaryRepresentation(className); + ClassReader classReader = new ClassReader(binaryRepresentation); + HookMetadataBuilder hookMetadata = new HookMetadataBuilder(className); + classReader.accept(new ClassVisitor(Opcodes.ASM5) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && typeEquals(desc, io.promagent.annotations.Hook.class)) { + return new AnnotationValueCollector("instruments", hookMetadata::addInstruments, Opcodes.ASM5, super.visitAnnotation(desc, visible)); + } else { + return super.visitAnnotation(desc, visible); + } + } + + @Override + public MethodVisitor visitMethod(int i, String method, String desc, String signature, String[] strings) { + List parameterTypes = Arrays.stream(Type.getArgumentTypes(desc)) + .map(Type::getClassName) + .collect(Collectors.toList()); + MethodSignatureBuilder builder = hookMetadata.newMethodSignature(parameterTypes); + return new MethodVisitor(Opcodes.ASM5, super.visitMethod(i, method, desc, signature, strings)) { + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { + if (visible && typeEquals(desc, Returned.class, Thrown.class)) { + builder.markReturnedOrThrown(parameter); + } + return super.visitParameterAnnotation(parameter, desc, visible); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && typeEquals(desc, Before.class, After.class)) { + return new AnnotationValueCollector("method", builder::addMethodName, Opcodes.ASM5, super.visitAnnotation(desc, visible)); + } else { + return super.visitAnnotation(desc, visible); + } + } + }; + } + + }, ClassReader.SKIP_CODE); + if (hookMetadata.isComplete()) { + result.add(hookMetadata.build()); + } + } + return result; + } + + private byte[] readBinaryRepresentation(String className) throws ClassNotFoundException { + String classFileName = "/" + className.replace(".", "/") + ".class"; + try(InputStream stream = getResourceAsStream(classFileName)) { + if (stream == null) { + throw new ClassNotFoundException(className); + } + return IOUtils.toByteArray(stream); + } catch (IOException e) { + throw new ClassNotFoundException(className, e); + } + } + + private InputStream getResourceAsStream(String name) throws IOException { + for (Path hookJar : hookJars) { + try { + URL url; + // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. + if (Files.isDirectory(hookJar)) { + url = hookJar.toUri().resolve("." + name).toURL(); + } else if (Files.isRegularFile(hookJar)) { + url = new URL("jar:" + hookJar.toUri().toURL().toString() + "!" + name); + } else { + throw new IOException("Invalid JAR file or classes directory: " + hookJar); + } + return url.openStream(); + } catch (FileNotFoundException e) {} + } + throw new FileNotFoundException(name + " not found in [" + hookJars.stream().map(Path::toString).reduce("", (s1, s2) -> s1 + ", " + s2) + "]"); + } + + private boolean typeEquals(String typeDescriptor, Class... classes) { + for (Class clazz : classes) { + if (clazz.getName().equals(Type.getType(typeDescriptor).getClassName())) { + return true; + } + } + return false; + } + + + /** + * Helper to create {@link HookMetadata} isntances. + */ + private static class HookMetadataBuilder { + + private String hookClassName; + private List instruments = new ArrayList<>(); + private List methods = new ArrayList<>(); + + private HookMetadataBuilder(String hookClassName) { + this.hookClassName = hookClassName; + } + + private void addInstruments(String instruments) { + this.instruments.add(instruments); + } + + private MethodSignatureBuilder newMethodSignature(List parameterTypes) { + MethodSignatureBuilder builder = new MethodSignatureBuilder(); + for (String parameterType : parameterTypes) { + builder.addParameterType(parameterType); + } + methods.add(builder); + return builder; + } + + private HookMetadata build() { + SortedSet methodSignatures = new TreeSet<>(); + for (MethodSignatureBuilder builder : methods) { + methodSignatures.addAll(builder.build()); + } + return new HookMetadata(hookClassName, instruments, methodSignatures); + } + + private boolean isComplete() { + return hookClassName != null && instruments.size() > 0 && methods.size() > 0; + } + } + + /** + * Helper to create {@link MethodSignature} instances. + *

+ * There's a slight mismatch between a {@link MethodSignatureBuilder} and a {@link MethodSignature}: + * While a {@link MethodSignature} can only contain a single method name, + * the {@link MethodSignatureBuilder} may have multiple method names. + * Therefore, the {@link #build()} method might create multiple {@link MethodSignature} instances + * out of a single {@link MethodSignatureBuilder}. + */ + private static class MethodSignatureBuilder { + + private static class ParameterType { + final String type; + boolean isReturnedOrThrown; // method parameters annotated with @Returned or @Thrown will be ignored. + + private ParameterType(String type) { + this.type = type; + this.isReturnedOrThrown = false; + } + } + + SortedSet methodNames = new TreeSet<>(); + List parameterTypes = new ArrayList<>(); + + private void addMethodName(String methodName) { + methodNames.add(methodName); + } + + private void addParameterType(String parameterType) { + parameterTypes.add(new ParameterType(parameterType)); + } + + private SortedSet build() { + List strippedParameterTypes = parameterTypes.stream() + .filter(p -> ! p.isReturnedOrThrown) + .map(p -> p.type) + .collect(Collectors.toList()); + SortedSet result = new TreeSet<>(); + for (String methodName : methodNames) { + result.add(new MethodSignature(methodName, strippedParameterTypes)); + } + return result; + } + + public void markReturnedOrThrown(int parameter) { + // We know that parameter is a valid index in parameterTypes. + parameterTypes.get(parameter).isReturnedOrThrown = true; + } + } + + /** + * Collect annotation values from @Hook(instruments = ...) or @Before(method = ...) or @After(method = ...) + *

+ * Example: If the annotation is @Before(method={"service", "doFilter"}) + * then using a AnnotationValueCollector with methodName=="method" will add the call + * consumer.accept("service") and consumer.accept("doFilter") + */ + private static class AnnotationValueCollector extends AnnotationVisitor { + private final Consumer consumer; + private final String methodName; + private AnnotationValueCollector(String methodName, Consumer consumer, int api, AnnotationVisitor av) { + super(api, av); + this.consumer = consumer; + this.methodName = methodName; + } + @Override + public AnnotationVisitor visitArray(String name) { + if (methodName.equals(name)) { + return new AnnotationVisitor(Opcodes.ASM5, super.visitArray(name)) { + @Override + public void visit(String name, Object value) { + consumer.accept(value.toString()); + } + }; + } else { + return super.visitArray(name); + } + } + } + + /** + * List all Java classes found in the JAR files. + * + */ + private static Set listAllJavaClasses(Set hookJars, Predicate classNameFilter) throws IOException { + Set result = new TreeSet<>(); + for (Path hookJar : hookJars) { + // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. + if (hookJar.toFile().isDirectory()) { + try (Stream dirEntries = Files.walk(hookJar)) { + addClassNames(dirEntries.map(hookJar::relativize).map(Path::toString), result, classNameFilter); + } + } + else if (hookJar.toFile().isFile()) { + try (ZipFile zipFile = new ZipFile(hookJar.toFile())) { + addClassNames(zipFile.stream().map(ZipEntry::getName), result, classNameFilter); + } + } else { + throw new IOException(hookJar + ": Failed to read file or directory."); + } + } + return result; + } + + /** + * Convert class file paths to class names and add them to result. + */ + private static void addClassNames(Stream paths, Collection result, Predicate classNameFilter) { + paths + .filter(name -> name.endsWith(".class")) + .map(name -> name.substring(0, name.length() - ".class".length())) + .map(name -> name.startsWith("/") ? name.substring(1) : name) + .map(name -> name.replace("/", ".")) + .filter(classNameFilter) + .collect(Collectors.toCollection(() -> result)); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java new file mode 100644 index 0000000..4323f0d --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java @@ -0,0 +1,155 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import static net.bytebuddy.matcher.ElementMatchers.isAbstract; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import javax.management.ObjectName; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.HookMetadata.MethodSignature; +import io.promagent.internal.jmx.Exporter; +import io.promagent.internal.jmx.PromagentCollectorRegistry; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class Promagent { + + public static void premain(String agentArgs, Instrumentation inst) { + try { + PromagentCollectorRegistry registry = new PromagentCollectorRegistry(); + ManagementFactory.getPlatformMBeanServer().registerMBean(new Exporter(registry), new ObjectName("io.promagent:type=exporter")); + Map args = parseCmdline(agentArgs); + if (args.containsKey("port")) { + BuiltInServer.run(args.get("host"), args.get("port"), registry); + } + ClassLoaderCache classLoaderCache = ClassLoaderCache.getInstance(); + List hookJars = classLoaderCache.getPerDeploymentJars(); + SortedSet hookMetadata = new HookMetadataParser(hookJars).parse(); + MetricsStore metricsStore = new MetricsStore(registry); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + printHookMetadata(hookMetadata); + + AgentBuilder agentBuilder = new AgentBuilder.Default(); + agentBuilder = applyHooks(agentBuilder, hookMetadata, classLoaderCache); + agentBuilder + .disableClassFormatChanges() + // .with(AgentBuilder.Listener.StreamWriting.toSystemError()) // use this to see exceptions thrown in instrumented code + .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) + .with(AgentBuilder.TypeStrategy.Default.REDEFINE) + .installOn(inst); + + // TODO -- the following is an experiment supporting collectors directly (in addition to hooks) + // io.prometheus.client.Collector jmxCollector = (io.prometheus.client.Collector) classLoaderCache.currentClassLoader().loadClass("io.promagent.collectors.JmxCollector").newInstance(); + // registry.registerNoJmx(jmxCollector); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * Add {@link ElementMatcher} for the hooks. + */ + private static AgentBuilder applyHooks(AgentBuilder agentBuilder, SortedSet hookMetadata, ClassLoaderCache classLoaderCache) { + Map> instruments = getInstruments(hookMetadata); + for (Map.Entry> entry : instruments.entrySet()) { + String instrumentedClassName = entry.getKey(); + Set instrumentedMethods = entry.getValue(); + agentBuilder = agentBuilder + .type(ElementMatchers.hasSuperType(named(instrumentedClassName))) + .transform(new AgentBuilder.Transformer.ForAdvice() + .include(classLoaderCache.currentClassLoader()) // must be able to load PromagentAdvice + .advice(matchAnyMethodIn(instrumentedMethods), PromagentAdvice.class.getName()) + ); + } + return agentBuilder; + } + + /** + * key: name of instrumented class or interface, value: set of instrumented methods for that class or interface + */ + public static Map> getInstruments(Set hooks) { + Map> result = new TreeMap<>(); + for (HookMetadata hookMetadata : hooks) { + for (String instruments : hookMetadata.getInstruments()) { + if (!result.containsKey(instruments)) { + result.put(instruments, new TreeSet<>()); + } + result.get(instruments).addAll(hookMetadata.getMethods()); + } + } + return result; + } + + /** + * Returns a byte buddy matcher matching any method contained in methodSignatures. + */ + public static ElementMatcher matchAnyMethodIn(Set methodSignatures) { + ElementMatcher.Junction methodMatcher = ElementMatchers.none(); + for (MethodSignature methodSignature : methodSignatures) { + ElementMatcher.Junction junction = ElementMatchers + .named(methodSignature.getMethodName()) + .and(not(isAbstract())) + .and(takesArguments(methodSignature.getParameterTypes().size())); + for (int i = 0; i < methodSignature.getParameterTypes().size(); i++) { + junction = junction.and(takesArgument(i, named(methodSignature.getParameterTypes().get(i)))); + } + methodMatcher = methodMatcher.or(junction); + } + return methodMatcher; + } + + /** + * Parse a comma-separated list of key/value pairs. Example: "host=localhost,port=9300" + */ + private static Map parseCmdline(String agentArgs) { + Map result = new HashMap<>(); + if (agentArgs != null) { + for (String keyValueString : agentArgs.split(",")) { + String[] keyValue = keyValueString.split("="); + if (keyValue.length != 2) { + throw new RuntimeException("Failed to parse command line arguments '" + agentArgs + "'. " + + "Expecting a comma-separated list of key/value pairs, as for example 'host=localhost,port=9300'."); + } + result.put(keyValue[0], keyValue[1]); + } + } + return result; + } + + private static void printHookMetadata(SortedSet hookMetadata) { + System.out.println("Promagent instrumenting the following classes or interfaces:"); + for (HookMetadata m : hookMetadata) { + System.out.println(m); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java new file mode 100644 index 0000000..7405db4 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java @@ -0,0 +1,78 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import io.promagent.agent.ClassLoaderCache; +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +import java.lang.reflect.Method; +import java.util.List; + +import static net.bytebuddy.asm.Advice.*; + +public class PromagentAdvice { + + // TODO: + // Should we move this class into it's own maven module + // to make clear that it cannot reference other classes from promagent-internal? + + @OnMethodEnter + @SuppressWarnings("unchecked") + public static List before( + @This(optional = true) Object that, + @Origin Method method, + @AllArguments Object[] args + ) { + // that is null when instrumenting static methods. + Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); + try { + // The following code is equivalent to: + // return Delegator.before(that, method, args); + // However, the Delegator class will not be available in the context of the instrumented method, + // so we must use our agent class loader to load the Delegator class and do the call via reflection. + Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.promagent.internal.Delegator"); + Method beforeMethod = delegator.getMethod("before", Class.class, Method.class, Object[].class); + return (List) beforeMethod.invoke(null, clazz, method, args); + } catch (Exception e) { + System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); + e.printStackTrace(); + return null; + } + } + + @OnMethodExit(onThrowable = Throwable.class) + public static void after( + @Enter List hooks, + @This(optional = true) Object that, + @Origin Method method, + @AllArguments Object[] args, + @Return(typing = Assigner.Typing.DYNAMIC) Object returned, // support void == null and int == Integer + @Thrown Throwable thrown + ) { + try { + // The following code is equivalent to: + // Delegator.after(hooks, method, args); + // However, the Delegator class will not be available in the context of the instrumented method, + // so we must use our agent class loader to load the Delegator class and do the call via reflection. + Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.promagent.internal.Delegator"); + Method afterMethod = delegator.getMethod("after", List.class, Method.class, Object[].class, Object.class, Throwable.class); + afterMethod.invoke(null, hooks, method, args, returned, thrown); + } catch (Exception e) { + Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); + System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); + e.printStackTrace(); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java new file mode 100644 index 0000000..9181dfc --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java @@ -0,0 +1,41 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.jmx; + +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import java.io.IOException; +import java.io.StringWriter; + +public class Exporter implements ExporterMBean { + + private final CollectorRegistry registry; + + public Exporter(CollectorRegistry registry) { + this.registry = registry; + } + + @Override + public String getTextFormat() { + try { + StringWriter result = new StringWriter(); + TextFormat.write004(result, registry.metricFamilySamples()); + return result.toString(); + } catch (IOException e) { + throw new RuntimeException("Unexpected error when writing metrics to a String: " + e.getMessage(), e); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java new file mode 100644 index 0000000..edba98e --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java @@ -0,0 +1,19 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.jmx; + +public interface ExporterMBean { + String getTextFormat(); +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java new file mode 100644 index 0000000..2466f86 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java @@ -0,0 +1,47 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.jmx; + +import io.prometheus.client.Collector; + +import java.util.HashMap; +import java.util.Map; + +public class Metric implements MetricMBean { + + private final Collector metric; + + Metric(Collector metric) { + this.metric = metric; + } + + /** + * @see MetricMBean#getValues() + */ + @Override + public Map, Double> getValues() { + Map, Double> result = new HashMap<>(); + for (Collector.MetricFamilySamples samples : metric.collect()) { + for (Collector.MetricFamilySamples.Sample sample : samples.samples) { + Map labels = new HashMap<>(); + for (int i = 0; i < sample.labelNames.size(); i++) { + labels.put(sample.labelNames.get(i), sample.labelValues.get(i)); + } + result.put(labels, sample.value); + } + } + return result; + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java new file mode 100644 index 0000000..d80bc75 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java @@ -0,0 +1,28 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.jmx; + +import java.util.Map; + +public interface MetricMBean { + + /** + * Get the values in a representation that can be used in MXBeans in JMX. + *

+ * The result is a map of labels -> value. + * The labels themselves are represented as a key -> value map. + */ + Map, Double> getValues(); +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java new file mode 100644 index 0000000..045cb72 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java @@ -0,0 +1,66 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.jmx; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.SimpleCollector; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; + +/** + * This is like the regular {@link CollectorRegistry}, except that when you {@link #register(Collector)} a metric, + * the metric will also be registered as an MBean in the JMX platform server. + */ +public class PromagentCollectorRegistry extends CollectorRegistry { + + @Override + public void register(Collector metric) { + super.register(metric); + try { + ManagementFactory.getPlatformMBeanServer().registerMBean(new Metric(metric), makeObjectName((SimpleCollector) metric)); + } catch (Exception e) { + throw new RuntimeException("Failed to register Prometheus metric: " + e.getMessage(), e); + } + } + + private static ObjectName makeObjectName(SimpleCollector metric) throws MalformedObjectNameException { + return makeObjectName(getFullName(metric)); + } + + private static ObjectName makeObjectName(String fullname) throws MalformedObjectNameException { + return new ObjectName("io.promagent:type=metrics,name=" + fullname); + } + + private static String getFullName(SimpleCollector metric) { + // Unfortunately, there is no public API to get the 'fullname' of a metric. We use reflection to get it anyway. + try { + Field field = SimpleCollector.class.getDeclaredField("fullname"); + field.setAccessible(true); + return (String) field.get(metric); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException("Failed to access " + metric.getClass().getName() + ".fullname. " + + "This is probably because the internal implementation of the Prometheus client library has changed. " + + "You should adapt the Promagent accordingly.", e); + } + } + + public void registerNoJmx(Collector collector) { + super.register(collector); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java new file mode 100644 index 0000000..790c6a4 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java @@ -0,0 +1,114 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import io.promagent.annotations.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.SortedSet; + +class HookMetadataParserTest { + + private HookMetadataParser parser; + + @BeforeEach + void setUp() { + List classesDir = new ArrayList<>(); + classesDir.add(Paths.get(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath())); + this.parser = new HookMetadataParser(classesDir); + } + + @Hook(instruments = { + "javax.servlet.Servlet", + "javax.servlet.Filter" + }) + private static class ServletTestHook { + + @Before(method = {"service", "doFilter"}) + public void before(ServletRequest request, ServletResponse response) {} + + @After(method = {"service", "doFilter"}) + public void after(ServletRequest request, ServletResponse response) throws Exception {} + } + + @Hook(instruments = "com.example.Some") + private static class PrimitiveTypesTestHook { + + @Before(method = "arrayArgs") + void before(Object[] a, int[] b, String[] c) {} + + @Before(method = "noArgs") + void before() {} + + @Before(method = "primitiveArgs") + void before(boolean a, char b, byte c, short d, int f, float g, long h, double i) {} + + @Before(method = "boxedArgs") + void before(Boolean a, Character b, Byte c, Short d, Integer f, Float g, Long h, Double i) {} + } + + @Hook(instruments = "com.example.ReturnThrown") + private static class ReturnedAndThrownTestHook { + @After(method = "div") + void after(int a, int b, @Returned int result, @Thrown Throwable exception) {} + } + + @Test + void testServletHook() throws ClassNotFoundException, IOException { + String expected = ServletTestHook.class.getName() + " instruments [javax.servlet.Filter, javax.servlet.Servlet]:\n" + + " * doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)\n" + + " * service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)"; + SortedSet result = parser.parse(className -> className.equals(ServletTestHook.class.getName())); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(expected, result.first().toString()); + } + + @Test + void testPrimitiveTypes() throws ClassNotFoundException, IOException { + String expected = PrimitiveTypesTestHook.class.getName() + " instruments [com.example.Some]:\n" + + " * arrayArgs(java.lang.Object[], int[], java.lang.String[])\n" + + " * boxedArgs(java.lang.Boolean, java.lang.Character, java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Float, java.lang.Long, java.lang.Double)\n" + + " * noArgs()\n" + + " * primitiveArgs(boolean, char, byte, short, int, float, long, double)"; + SortedSet result = parser.parse(className -> className.equals(PrimitiveTypesTestHook.class.getName())); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(expected, result.first().toString()); + } + + @Test + void testReturnedAndThrown() throws IOException, ClassNotFoundException { + String expected = ReturnedAndThrownTestHook.class.getName() + " instruments [com.example.ReturnThrown]:\n" + + " * div(int, int)"; + SortedSet result = parser.parse(className -> className.equals(ReturnedAndThrownTestHook.class.getName())); + Assertions.assertEquals(1, result.size()); + Assertions.assertEquals(expected, result.first().toString()); + } + + @Test + void testNoHook() throws ClassNotFoundException, IOException { + // Use HookMetadataParserTest as an example of a class that does not have any @Hook annotation. + SortedSet result = parser.parse(className -> className.equals(HookMetadataParserTest.class.getName())); + Assertions.assertTrue(result.isEmpty()); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java new file mode 100644 index 0000000..5888cc4 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java @@ -0,0 +1,48 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class HookMetadataTest { + + private HookMetadata.LexicographicalComparator> comparator = new HookMetadata.LexicographicalComparator<>(); + + @Test + void testComparatorEqualSize() { + List list1 = Arrays.asList("a", "b", "c", "e"); + List list2 = Arrays.asList("a", "b", "d", "e"); + assertEquals(-1, comparator.compare(list1, list2)); + } + + @Test + void testComparatorDifferentSize() { + List list1 = Arrays.asList("a", "b", "c"); + List list2 = Arrays.asList("a", "b"); + assertEquals(1, comparator.compare(list1, list2)); + } + + @Test + void testComparatorEqual() { + List list1 = new ArrayList<>(); + List list2 = new ArrayList<>(); + assertEquals(0, comparator.compare(list1, list2)); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java new file mode 100644 index 0000000..9aad2c9 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java @@ -0,0 +1,38 @@ +package io.promagent.internal.instrumentationtests; + +import io.promagent.internal.HookMetadata; +import io.promagent.internal.Promagent; +import io.promagent.internal.PromagentAdvice; +import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; + +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import static io.promagent.internal.Promagent.getInstruments; + +/** + * Create an instrumented version of {@link ParameterTypesExample} for {@link ParameterTypesTest}. + * The implementation is as close as possible to the instrumentation in {@link Promagent}. + */ +public class Instrumentor { + + /** + * Returns a copy of {@link ParameterTypesExample} which will be instrumented and loaded from a temporary class loader. + */ + public static T instrument(Class classToBeInstrumented, SortedSet hookMetadata) throws Exception { + Map> instruments = getInstruments(hookMetadata); + Set instrumentedMethods = instruments.get(classToBeInstrumented.getName()); + // For examples of byte buddy tests, see net.bytebuddy.asm.AdviceTest in the byte buddy source code. + return new ByteBuddy() + .redefine(classToBeInstrumented) + .visit(Advice.to(PromagentAdvice.class).on(Promagent.matchAnyMethodIn(instrumentedMethods))) + .make() + .load(Instrumentor.class.getClassLoader(), ClassLoadingStrategy.Default.CHILD_FIRST) + .getLoaded() + .newInstance(); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java new file mode 100644 index 0000000..84babc9 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java @@ -0,0 +1,112 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.instrumentationtests; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.Delegator; +import io.promagent.internal.HookMetadata; +import io.promagent.internal.instrumentationtests.classes.Fruit; +import io.promagent.internal.instrumentationtests.classes.IParameterTypesExample; +import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; +import io.promagent.internal.instrumentationtests.hooks.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +class LifecycleTest { + + private IParameterTypesExample parameterTypesExample; + + @BeforeEach + void setUp() throws Exception { + SortedSet hookMetadata = Util.loadHookMetadata( + LifecycleHookSkipTrue.class, + LifecycleHookSkipFalse.class + ); + ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); + parameterTypesExample = Instrumentor.instrument(ParameterTypesExample.class, hookMetadata); + MetricsStore metricsStore = Util.mockMetricsStore(); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + MethodCallCounter.reset(); + } + + /** + * Expected behavior: + *

    + *
  • For LifecycleHookSkipTrue, only the first call to parameterTypesExample.recursive() should be instrumented, + * all recursive calls should not be instrumented. + *
  • For LifecycleHookSkipFalse, all calls should be instrumented, but recursive calls are executed with the existing Hook instance, + * while non-recursive calls are executed with a new instance. + *
+ */ + @Test + void testLivecycle() throws Exception { + List runConfigs = Arrays.asList( + new RecursiveRunConfig(3, 4), + new RecursiveRunConfig(2, 3), + new RecursiveRunConfig(2, 4) + ); + ExecutorService executor = Executors.newFixedThreadPool(runConfigs.size()); + for (RecursiveRunConfig runConfig : runConfigs) { + executor.submit(() -> { + for (int i=0; i cfg.nRecursiveCalls) + .max() + .getAsInt(); + + for (int nRecursiveCalls = maxNRecursiveCalls; nRecursiveCalls >= 0; nRecursiveCalls--) { + final int n = nRecursiveCalls; // copy to final variable so it can be used in lambda + int expectedNumCallsSkipFalse = runConfigs.stream() + .filter(cfg -> cfg.nRecursiveCalls >= n) // recursive calls executed: >= n + .mapToInt(cfg -> cfg.nRuns) + .sum(); + int expectedNumCallsSkipTrue = runConfigs.stream() + .filter(cfg -> cfg.nRecursiveCalls == n) // recursive calls skipped: == n + .mapToInt(cfg -> cfg.nRuns) + .sum(); + MethodCallCounter.assertNumCalls(expectedNumCallsSkipFalse, LifecycleHookSkipFalse.class, "before", nRecursiveCalls); + MethodCallCounter.assertNumCalls(expectedNumCallsSkipTrue, LifecycleHookSkipTrue.class, "before", nRecursiveCalls); + } + // Number of instances should be the same because recursive calls are executed with an existing instance. + MethodCallCounter.assertNumHookInstances(runConfigs.stream().mapToInt(cfg -> cfg.nRuns).sum(), LifecycleHookSkipFalse.class); + MethodCallCounter.assertNumHookInstances(runConfigs.stream().mapToInt(cfg -> cfg.nRuns).sum(), LifecycleHookSkipTrue.class); + } + + private static class RecursiveRunConfig { + final int nRuns; // number of runs within the same thread + final int nRecursiveCalls; // number of recursive calls within each run + + private RecursiveRunConfig(int nRuns, int nRecursiveCalls) { + this.nRuns = nRuns; + this.nRecursiveCalls = nRecursiveCalls; + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java new file mode 100644 index 0000000..0153274 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java @@ -0,0 +1,66 @@ +package io.promagent.internal.instrumentationtests; + +import org.junit.jupiter.api.Assertions; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Track method calls, used in {@link ParameterTypesTest} + */ +public class MethodCallCounter { + + public static void reset() { + captures.clear(); + } + + public static void observe(Object hook, String methodName, Object... args) { + captures.add(new Capture(hook, methodName, args)); + } + + public static void assertNumCalls(int expectedNumberOfCalls, Class hookClass, String hookMethod, Object... expectedArgs) { + List matching = captures.stream() + .filter(c -> c.hook.getClass().equals(hookClass)) + .filter(c -> c.hookMethod.equals(hookMethod)) + .filter(c -> Arrays.equals(expectedArgs, c.args)) + .collect(Collectors.toList()); + Assertions.assertEquals(expectedNumberOfCalls, matching.size()); + } + + // special case for the varargsMixed test + public static void assertNumCalls(int expectedNumberOfCalls, Class hookClass, String hookMethod, String firstString, String... moreStrings) { + List matching = captures.stream() + .filter(c -> c.hook.getClass().equals(hookClass)) + .filter(c -> c.hookMethod.equals(hookMethod)) + .filter(c -> c.args.length == 2) + .filter(c -> Objects.equals(firstString, c.args[0])) + .filter(c -> Arrays.equals(moreStrings, (String[]) c.args[1])) + .collect(Collectors.toList()); + Assertions.assertEquals(expectedNumberOfCalls, matching.size()); + } + + public static void assertNumHookInstances(int expectedNumberOfInstances, Class hookClass) { + long actualNumberOfInstances = captures.stream() + .filter(c -> c.hook.getClass().equals(hookClass)) + .map(c -> c.hook) + .distinct() + .count(); + Assertions.assertEquals(expectedNumberOfInstances, (int) actualNumberOfInstances); + } + + private static class Capture { + final Object hook; + final String hookMethod; + final Object[] args; + + Capture(Object hook, String hookMethod, Object[] args) { + this.hook = hook; + this.hookMethod = hookMethod; + this.args = args; + } + } + + private static final List captures = Collections.synchronizedList(new ArrayList<>()); + + private MethodCallCounter() {} +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java new file mode 100644 index 0000000..078f140 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java @@ -0,0 +1,238 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.instrumentationtests; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.Delegator; +import io.promagent.internal.HookMetadata; +import io.promagent.internal.instrumentationtests.classes.Fruit; +import io.promagent.internal.instrumentationtests.classes.IParameterTypesExample; +import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; +import io.promagent.internal.instrumentationtests.hooks.OnlyAfterHook; +import io.promagent.internal.instrumentationtests.hooks.OnlyBeforeHook; +import io.promagent.internal.instrumentationtests.hooks.ParameterTypesHook; +import io.promagent.internal.instrumentationtests.hooks.TwoHooks; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +class ParameterTypesTest { + + private IParameterTypesExample parameterTypesExample; + + private final byte b = (byte) 0x23; + private final short s = (short) 42; + private final int i = 7; + private final long l = 3L; + private final float f = 0.4f; + private final double d = 0.5d; + private final boolean x = true; + private final char c = 'a'; + + private final Fruit.Orange obj1 = new Fruit.Orange(); + private final Fruit.Orange obj2 = new Fruit.Orange(); + private final Fruit.Orange obj3 = new Fruit.Orange(); + + @BeforeEach + void setUp() throws Exception { + SortedSet hookMetadata = Util.loadHookMetadata( + ParameterTypesHook.class, + TwoHooks.HookOne.class, + TwoHooks.HookTwo.class, + OnlyBeforeHook.class, + OnlyAfterHook.class + ); + ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); + parameterTypesExample = Instrumentor.instrument(ParameterTypesExample.class, hookMetadata); + MetricsStore metricsStore = Util.mockMetricsStore(); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + MethodCallCounter.reset(); + } + + @Test + void testNullArgs() { + for (int n=1; n<=2; n++) { + parameterTypesExample.objects(null, obj2, null); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", null, obj2, null); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", null, obj2, null); + } + } + + @Test + void testBeforeOrAfterMissing() { + for (int n=1; n<=2; n++) { + parameterTypesExample.objects(obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, OnlyBeforeHook.class, "before", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, OnlyAfterHook.class, "after", obj1, obj2, obj3); + } + } + + @Test + void testNoArg() { + for (int i=1; i<=2; i++) { + parameterTypesExample.noParam(); + MethodCallCounter.assertNumCalls(i, ParameterTypesHook.class, "before"); + MethodCallCounter.assertNumCalls(i, ParameterTypesHook.class, "after"); + } + } + + @Test + void testPrimitiveTypes() { + for (int n=1; n<=2; n++) { + parameterTypesExample.primitiveTypes(b, s, i, l, f, d, x, c); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", b, s, i, l, f, d, x, c); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", b, s, i, l, f, d, x, c); + } + } + + @Test + void testBoxedTypes() { + for (int n=1; n<=2; n++) { + parameterTypesExample.boxedTypes(b, s, i, l, f, d, x, c); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", b, s, i, l, f, d, x, c); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", b, s, i, l, f, d, x, c); + } + } + + @Test + void testObjects() { + for (int n=1; n<=2; n++) { + parameterTypesExample.objects(obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before2", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after2", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(0, ParameterTypesHook.class, "beforeTooLoose", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(0, ParameterTypesHook.class, "beforeTooStrict", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(2*n, ParameterTypesHook.class, "beforeAndAfter", obj1, obj2, obj3); + } + } + + @Test + void testTwoHooks() { + for (int n=1; n<=2; n++) { + parameterTypesExample.objects(obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, TwoHooks.HookOne.class, "before", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, TwoHooks.HookTwo.class, "before", obj1, obj2, obj3); + } + } + + @Test + void testPrimitiveArrays() { + byte[] byteArray = new byte[] {b}; + short[] shortArray = new short[] {s}; + int[] intArray = new int[] {i}; + long[] longArray = new long[] {l}; + float[] floatArray = new float[] {f}; + double[] doubleArray = new double[] {d}; + boolean[] booleanArray = new boolean[] {x}; + char[] charArray = new char[] {c}; + for (int n=1; n<=2; n++) { + parameterTypesExample.primitiveArrays(byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); + } + } + + @Test + void testBoxedArrays() { + Byte[] byteArray = new Byte[] {b}; + Short[] shortArray = new Short[] {s}; + Integer[] intArray = new Integer[] {i}; + Long[] longArray = new Long[] {l}; + Float[] floatArray = new Float[] {f}; + Double[] doubleArray = new Double[] {d}; + Boolean[] booleanArray = new Boolean[] {x}; + Character[] charArray = new Character[] {c}; + for (int n=1; n<=2; n++) { + parameterTypesExample.boxedArrays(byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); + } + } + + @Test + void testObjectArrays() { + Object[] arr1 = new Object[] {obj1, obj2}; + Fruit[] arr2 = new Fruit[] {obj3}; + Fruit.Orange[] arr3 = new Fruit.Orange[0]; + for (int n=1; n<=2; n++) { + parameterTypesExample.objectArrays(arr1, arr2, arr3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr1, arr2, arr3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr1, arr2, arr3); + } + } + + @Test + void testGenerics() { + List list1 = Arrays.asList(obj1, obj2); + List list2 = Collections.singletonList(obj3); + List list3 = new ArrayList<>(); + for (int n=1; n<=2; n++) { + parameterTypesExample.generics(list1, list2, list3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", list1, list2, list3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", list1, list2, list3); + } + } + + @Test + void testVarargsExplicit() { + for (int n=1; n<=2; n++) { + parameterTypesExample.varargsExplicit(obj1, obj2, obj3); + parameterTypesExample.varargsExplicit(); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before"); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after"); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", obj1, obj2, obj3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", obj1, obj2, obj3); + } + } + + @Test + void testVarargsImplicit() { + Object[] arr1 = new Object[] {obj1}; + Object[] arr2 = new Object[] {}; + Object[] arr3 = null; + for (int n=1; n<=2; n++) { + parameterTypesExample.varargsImplicit(arr1); + parameterTypesExample.varargsImplicit(arr2); + parameterTypesExample.varargsImplicit(arr3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr1); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr1); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr2); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr2); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr3); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr3); + } + } + + @Test + void testVarargsMixed() { + for (int n=1; n<=2; n++) { + parameterTypesExample.varargsMixed("hello"); + parameterTypesExample.varargsMixed("hello", "world"); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", "hello"); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", "hello"); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", "hello", "world"); + MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", "hello", "world"); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java new file mode 100644 index 0000000..4efd88c --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java @@ -0,0 +1,120 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.internal.instrumentationtests; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.Delegator; +import io.promagent.internal.HookMetadata; +import io.promagent.internal.instrumentationtests.classes.Fruit; +import io.promagent.internal.instrumentationtests.classes.IReturnedAndThrownExample; +import io.promagent.internal.instrumentationtests.classes.ReturnedAndThrownExample; +import io.promagent.internal.instrumentationtests.hooks.ReturnedAndThrownHook; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.SortedSet; + +class ReturnedAndThrownTest { + + private IReturnedAndThrownExample returnedAndThrownExample; + private final Fruit.Orange orange = new Fruit.Orange(); + + @BeforeEach + void setUp() throws Exception { + SortedSet hookMetadata = Util.loadHookMetadata( + ReturnedAndThrownHook.class + ); + ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); + returnedAndThrownExample = Instrumentor.instrument(ReturnedAndThrownExample.class, hookMetadata); + MetricsStore metricsStore = Util.mockMetricsStore(); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + MethodCallCounter.reset(); + } + + @Test + void testVoid() { + for (int n=1; n<=2; n++) { + returnedAndThrownExample.returnVoid(orange); + MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "beforeVoid", orange); + MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "afterVoid", orange); + } + } + + @Test + void testPrimitive() { + for (int n=1; n<=2; n++) { + int ret = returnedAndThrownExample.returnPrimitive(orange); + MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "beforePrimitive", orange); + MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "afterPrimitive", orange, ret); + } + } + + @Test + void testObject() { + Fruit ret1 = returnedAndThrownExample.returnObject(); + Fruit ret2 = returnedAndThrownExample.returnObject(); + MethodCallCounter.assertNumCalls(2, ReturnedAndThrownHook.class, "beforeObject"); + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterObject", ret1); + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterObject", ret2); + } + + @Test + void testArray() { + int[] params = {1, 2, 3}; + int[] ret1 = returnedAndThrownExample.returnArray(params); + int[] ret2 = returnedAndThrownExample.returnArray(params); + MethodCallCounter.assertNumCalls(2, ReturnedAndThrownHook.class, "beforeArray", new Object[]{params}); + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterArray", ret1, params); + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterArray", ret2, params); + } + + @Test + void testGenerics() { + for (int n=1; n<=2; n++) { + List ret = returnedAndThrownExample.returnGenerics(orange); + MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "beforeGenerics", orange); + // The following works because List.equals() compares the elements within the lists, not the lists itself. + // Therefore both return values are considered equal, because they are both lists containing the same element. + MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "afterGenerics", orange, ret); + } + } + + @Test + void testRuntimeException() { + try { + returnedAndThrownExample.throwsRuntimeException(7, orange); + } catch (Exception e) { + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "beforeThrowsRuntimeException", 7, orange); + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterThrowsRuntimeException", 7, orange, null, e); + return; // success + } + Assertions.fail("exception not thrown."); + } + + @Test + void testCheckedException() { + try { + returnedAndThrownExample.throwsCheckedException(); + } catch (Exception e) { + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "beforeThrowsCheckedException"); + MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterThrowsCheckedException", 0, e); + return; // success + } + Assertions.fail("exception not thrown."); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java new file mode 100644 index 0000000..c27d58d --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java @@ -0,0 +1,66 @@ +package io.promagent.internal.instrumentationtests; + +import static io.promagent.internal.Promagent.getInstruments; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.Delegator; +import io.promagent.internal.HookMetadata; +import io.promagent.internal.Promagent; +import io.promagent.internal.PromagentAdvice; +import io.promagent.internal.instrumentationtests.classes.StaticFinalExample; +import io.promagent.internal.instrumentationtests.hooks.StaticFinalTestHook; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class StaticFinalTest { + + private Object example; + + @BeforeEach + void setUp() throws Exception { + SortedSet hookMetadata = Util.loadHookMetadata(StaticFinalTestHook.class); + ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); + + MetricsStore metricsStore = Util.mockMetricsStore(); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + MethodCallCounter.reset(); + + Map> instruments = getInstruments(hookMetadata); + Set instrumentedMethods = instruments.get(StaticFinalExample.class.getName()); + example = new ByteBuddy() + .redefine(StaticFinalExample.class) + .visit(Advice.to(PromagentAdvice.class).on(Promagent.matchAnyMethodIn(instrumentedMethods))) + .make() + .load(this.getClass().getClassLoader(), ClassLoadingStrategy.Default.CHILD_FIRST) + .getLoaded() + .newInstance(); + } + + @Test + void testPublicStaticMethod() throws Exception { + int expectedTotalHookCalls = 0; + for (String methodName : new String[] { + "helloPublic", + "helloPublicFinal", + "helloPublicStatic", + "helloPublicStaticFinal" + }) { + Method method = example.getClass().getMethod(methodName, String.class); + method.invoke(example, "world"); + expectedTotalHookCalls++; + MethodCallCounter.assertNumCalls(expectedTotalHookCalls, StaticFinalTestHook.class, "before", new Object[]{"world"}); + MethodCallCounter.assertNumCalls(expectedTotalHookCalls, StaticFinalTestHook.class, "after", new Object[]{"world"}); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java new file mode 100644 index 0000000..e23cbe7 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java @@ -0,0 +1,39 @@ +package io.promagent.internal.instrumentationtests; + +import io.promagent.agent.ClassLoaderCache; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.HookMetadata; +import io.promagent.internal.HookMetadataParser; +import org.mockito.Mockito; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.SortedSet; +import java.util.stream.Stream; + +class Util { + + static SortedSet loadHookMetadata(Class... hooks) throws IOException, ClassNotFoundException { + List classesDir = new ArrayList<>(); + classesDir.add(Paths.get(Util.class.getProtectionDomain().getCodeSource().getLocation().getPath())); + HookMetadataParser parser = new HookMetadataParser(classesDir); + return parser.parse(className -> Stream.of(hooks).anyMatch(hookClass -> hookClass.getName().equals(className))); + } + + static ClassLoaderCache mockClassLoaderCache() throws NoSuchFieldException, IllegalAccessException { + ClassLoaderCache mockedClassLoaderCache = Mockito.mock(ClassLoaderCache.class); + Mockito.when(mockedClassLoaderCache.currentClassLoader()).thenReturn(Thread.currentThread().getContextClassLoader()); + Field instance = ClassLoaderCache.class.getDeclaredField("instance"); + instance.setAccessible(true); + instance.set(null, mockedClassLoaderCache); + return mockedClassLoaderCache; + } + + static MetricsStore mockMetricsStore() { + return Mockito.mock(MetricsStore.class); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java new file mode 100644 index 0000000..4d289a5 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java @@ -0,0 +1,8 @@ +package io.promagent.internal.instrumentationtests.classes; + +/** + * Example of object oriented inheritance. + */ +public class Fruit { + public static class Orange extends Fruit{} +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java new file mode 100644 index 0000000..324f72c --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java @@ -0,0 +1,39 @@ +package io.promagent.internal.instrumentationtests.classes; + +import io.promagent.internal.instrumentationtests.Instrumentor; + +import java.util.List; + +/** + * Example methods to be instrumented with hooks. + * We use an interface to call these methods, because the actual implementation will come from a temporary + * class loader defined in {@link Instrumentor}, which cannot be used directly. + */ +public interface IParameterTypesExample { + + // TODO: add tests for enums and lambdas + + void noParam(); + + void primitiveTypes(byte b, short s, int i, long l, float f, double d, boolean x, char c); + + void boxedTypes(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c); + + void objects(Object o, Fruit f, Fruit.Orange x); + + void primitiveArrays(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c); + + void boxedArrays(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c); + + void objectArrays(Object[] o, Fruit[] f, Fruit.Orange[] x); + + void generics(List objectList, List fruitList, List orangeList); + + void varargsExplicit(Object... args); + + void varargsImplicit(Object[] args); + + void varargsMixed(String s, String... more); + + void recursive(int nRecursiveCalls); +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java new file mode 100644 index 0000000..5e513fd --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java @@ -0,0 +1,23 @@ +package io.promagent.internal.instrumentationtests.classes; + +import java.io.IOException; +import java.util.List; + +public interface IReturnedAndThrownExample { + + // TODO: Some more return types that should be tested: Enums, Lambdas + + void returnVoid(Fruit f); + + int returnPrimitive(Fruit.Orange orange); + + Fruit returnObject(); + + int[] returnArray(int... params); + + List returnGenerics(T fruit); + + String throwsRuntimeException(int a, Fruit.Orange b); + + int throwsCheckedException() throws IOException; +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java new file mode 100644 index 0000000..1f29564 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java @@ -0,0 +1,51 @@ +package io.promagent.internal.instrumentationtests.classes; + +import io.promagent.internal.instrumentationtests.Instrumentor; + +import java.util.List; + +/** + * These methods will be instrumented by {@link Instrumentor}. + */ +public class ParameterTypesExample implements IParameterTypesExample { + + @Override + public void noParam() {} + + @Override + public void primitiveTypes(byte b, short s, int i, long l, float f, double d, boolean x, char c) {} + + @Override + public void boxedTypes(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c) {} + + @Override + public void objects(Object o, Fruit f, Fruit.Orange x) {} + + @Override + public void primitiveArrays(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c) {} + + @Override + public void boxedArrays(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c) {} + + @Override + public void objectArrays(Object[] o, Fruit[] f, Fruit.Orange[] x) {} + + @Override + public void generics(List objectList, List fruitList, List orangeList) {} + + @Override + public void varargsExplicit(Object... args) {} + + @Override + public void varargsImplicit(Object[] args) {} + + @Override + public void varargsMixed(String s, String... more) {} + + @Override + public void recursive(int nRecursiveCalls) { + if (nRecursiveCalls > 0) { + recursive(nRecursiveCalls - 1); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java new file mode 100644 index 0000000..a6a66d5 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java @@ -0,0 +1,42 @@ +package io.promagent.internal.instrumentationtests.classes; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class ReturnedAndThrownExample implements IReturnedAndThrownExample { + + @Override + public void returnVoid(Fruit f) {} + + @Override + public int returnPrimitive(Fruit.Orange orange) { + return 42; + } + + @Override + public Fruit returnObject() { + return new Fruit.Orange(); + } + + @Override + public int[] returnArray(int... params) { + return new int[] {23, 42}; + } + + @Override + public List returnGenerics(T fruit) { + return Collections.singletonList(fruit); + } + + @Override + public String throwsRuntimeException(int a, Fruit.Orange b) { + Object n = null; + return n.toString(); // throws NullPointerException + } + + @Override + public int throwsCheckedException() throws IOException { + throw new IOException(); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java new file mode 100644 index 0000000..57dce6b --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java @@ -0,0 +1,20 @@ +package io.promagent.internal.instrumentationtests.classes; + +public class StaticFinalExample { + + public String helloPublic(String name) { + return "hello public " + name; + } + + public final String helloPublicFinal(String name) { + return "hello public final " + name; + } + + public static String helloPublicStatic(String name) { + return "hello public static " + name; + } + + public static String helloPublicStaticFinal(String name) { + return "hello public static final " + name; + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java new file mode 100644 index 0000000..d29fec0 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java @@ -0,0 +1,32 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; +import io.promagent.internal.instrumentationtests.classes.Fruit.Orange; +import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; + +/** + * Instrument all methods in {@link ParameterTypesExample}. + */ +@Hook( + instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample", + skipNestedCalls = false +) +public class LifecycleHookSkipFalse { + + public LifecycleHookSkipFalse(MetricsStore m) {} + + @Before(method = "recursive") + public void before(int n) { + MethodCallCounter.observe(this, "before", n); + } + + @After(method = "recursive") + public void after(int n) { + MethodCallCounter.observe(this, "after", n); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java new file mode 100644 index 0000000..73f808b --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java @@ -0,0 +1,34 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; +import io.promagent.internal.instrumentationtests.classes.Fruit.Orange; +import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; + +import java.util.List; + +/** + * Instrument all methods in {@link ParameterTypesExample}. + */ +@Hook( + instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample", + skipNestedCalls = true +) +public class LifecycleHookSkipTrue { + + public LifecycleHookSkipTrue(MetricsStore m) {} + + @Before(method = "recursive") + public void before(int n) { + MethodCallCounter.observe(this, "before", n); + } + + @After(method = "recursive") + public void after(int n) { + MethodCallCounter.observe(this, "after", n); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java new file mode 100644 index 0000000..76d3b10 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java @@ -0,0 +1,21 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; + +/** + * Test hook with no @Before method. + */ +@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") +public class OnlyAfterHook { + + public OnlyAfterHook(MetricsStore m) {} + + @After(method = "objects") + public void after(Object o, Fruit f, Fruit.Orange x) { + MethodCallCounter.observe(this, "after", o, f, x); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java new file mode 100644 index 0000000..d671514 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java @@ -0,0 +1,21 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; + +/** + * Test hook with no @After method + */ +@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") +public class OnlyBeforeHook { + + public OnlyBeforeHook(MetricsStore m) {} + + @Before(method = "objects") + public void before(Object o, Fruit f, Fruit.Orange x) { + MethodCallCounter.observe(this, "before", o, f, x); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java new file mode 100644 index 0000000..c9a724d --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java @@ -0,0 +1,167 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; +import io.promagent.internal.instrumentationtests.classes.Fruit.Orange; +import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; + +import java.util.List; + +/** + * Instrument all methods in {@link ParameterTypesExample}. + */ +@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") +public class ParameterTypesHook { + + public ParameterTypesHook(MetricsStore m) {} + + @Before(method = "noParam") + public void before() { + MethodCallCounter.observe(this, "before"); + } + + @After(method = "noParam") + public void after() { + MethodCallCounter.observe(this, "after"); + } + + @Before(method = {"primitiveTypes", "boxedTypes"}) // "boxedTypes" should be ignored because different method signature + public void before(byte b, short s, int i, long l, float f, double d, boolean x, char c) { + MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); + } + + @After(method = {"primitiveTypes", "boxedTypes"}) // "boxedTypes" should be ignored because different method signature + public void after(byte b, short s, int i, long l, float f, double d, boolean x, char c) { + MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); + } + + @Before(method = {"primitiveTypes", "boxedTypes"}) // "primitiveTypes" should be ignored because different method signature + public void before(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c) { + MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); + } + + @After(method = {"primitiveTypes", "boxedTypes"}) // "primitiveTypes" should be ignored because different method signature + public void after(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c) { + MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); + } + + @Before(method = "objects") + public void before(Object o, Fruit f, Orange x) { + MethodCallCounter.observe(this, "before", o, f, x); + } + + @After(method = "objects") + public void after(Object o, Fruit f, Orange x) { + MethodCallCounter.observe(this, "after", o, f, x); + } + + @Before(method = "objects") + public void before2(Object o, Fruit f, Orange x) { + MethodCallCounter.observe(this, "before2", o, f, x); + } + + @After(method = "objects") + public void after2(Object o, Fruit f, Orange x) { + MethodCallCounter.observe(this, "after2", o, f, x); + } + + @Before(method = "objects") // should not be called, because method signature differs + public void beforeTooLoose(Object o, Fruit f, Fruit x) { + MethodCallCounter.observe(this, "beforeTooLoose", o, f, x); + } + + @Before(method = "objects") // should not be called, because method signature differs + public void beforeTooStrict(Object o, Orange f, Orange x) { + MethodCallCounter.observe(this, "beforeTooStrict", o, f, x); + } + + @Before(method = "objects") + @After(method = "objects") + public void beforeAndAfter(Object o, Fruit f, Orange x) { + MethodCallCounter.observe(this, "beforeAndAfter", o, f, x); + } + + @Before(method = "primitiveArrays") + public void before(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c) { + MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); + } + + @After(method = "primitiveArrays") + public void after(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c) { + MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); + } + + @Before(method = "boxedArrays") + public void before(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c) { + MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); + } + + @After(method = "boxedArrays") + public void after(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c) { + MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); + } + + @Before(method = "objectArrays") + public void before(Object[] o, Fruit[] f, Orange[] x) { + MethodCallCounter.observe(this, "before", o, f, x); + } + + @After(method = "objectArrays") + public void after(Object[] o, Fruit[] f, Orange[] x) { + MethodCallCounter.observe(this, "after", o, f, x); + } + + @Before(method = "generics") + public void before(List o, List f, List x) { + MethodCallCounter.observe(this, "before", o, f, x); + } + + @After(method = "generics") + public void after(List o, List f, List x) { + MethodCallCounter.observe(this, "after", o, f, x); + } + + @Before(method = "varargsExplicit") + public void before(Object... args) { + MethodCallCounter.observe(this, "before", args); + } + + @After(method = "varargsExplicit") + public void after(Object... args) { + MethodCallCounter.observe(this, "after", args); + } + + @Before(method = "varargsImplicit") + public void before2(Object[] args) { + MethodCallCounter.observe(this, "before", args); + } + + @After(method = "varargsImplicit") + public void after2(Object[] args) { + MethodCallCounter.observe(this, "after", args); + } + + @Before(method = "varargsMixed") + public void before(String s, String... more) { + MethodCallCounter.observe(this, "before", s, more); + } + + @After(method = "varargsMixed") + public void after(String s, String... more) { + MethodCallCounter.observe(this, "after", s, more); + } + + @Before(method = "recursive") + public void before(int n) { + MethodCallCounter.observe(this, "before", n); + } + + @After(method = "recursive") + public void after(int n) { + MethodCallCounter.observe(this, "after", n); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java new file mode 100644 index 0000000..97435c7 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java @@ -0,0 +1,84 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.*; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; + +import java.util.List; + +@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ReturnedAndThrownExample") +public class ReturnedAndThrownHook { + + public ReturnedAndThrownHook(MetricsStore m) {} + + @Before(method = "returnVoid") + public void beforeVoid(Fruit f) { + MethodCallCounter.observe(this, "beforeVoid", f); + } + + @Before(method = "returnVoid") + public void afterVoid(Fruit f) { + MethodCallCounter.observe(this, "afterVoid", f); + } + + @Before(method = "returnPrimitive") + public void beforePrimitive(Fruit.Orange o) { + MethodCallCounter.observe(this, "beforePrimitive", o); + } + + @After(method = "returnPrimitive") + public void afterPrimitive(Fruit.Orange o, @Returned int i) { + MethodCallCounter.observe(this, "afterPrimitive", o, i); + } + + @Before(method = "returnObject") + public void beforeObject() { + MethodCallCounter.observe(this, "beforeObject"); + } + + @After(method = "returnObject") + public void afterObject(@Returned Fruit f) { + MethodCallCounter.observe(this, "afterObject", f); + } + + @Before(method = "returnArray") + public void beforeArray(int... params) { + MethodCallCounter.observe(this, "beforeArray", new Object[]{params}); + } + + @After(method = "returnArray") + public void afterArray(@Returned int[] ret, int... params) { + MethodCallCounter.observe(this, "afterArray", ret, params); + } + + @Before(method = "returnGenerics") + public void beforeGenerics(T fruit) { + MethodCallCounter.observe(this, "beforeGenerics", fruit); + } + + @After(method = "returnGenerics") + public void afterGenerics(T fruit, @Returned List ret) { + MethodCallCounter.observe(this, "afterGenerics", fruit, ret); + } + + @Before(method = "throwsRuntimeException") + public void beforeThrowsRuntimeException(int a, Fruit.Orange o) { + MethodCallCounter.observe(this, "beforeThrowsRuntimeException", a, o); + } + + @After(method = "throwsRuntimeException") + public void afterThrowsRuntimeException(int a, Fruit.Orange o, @Returned String ret, @Thrown Throwable e) { + MethodCallCounter.observe(this, "afterThrowsRuntimeException", a, o, ret, e); + } + + @Before(method = "throwsCheckedException") + public void beforeThrowsCheckedException() { + MethodCallCounter.observe(this, "beforeThrowsCheckedException"); + } + + @After(method = "throwsCheckedException") + public void afterThrowsCheckedException(@Returned int ret, @Thrown Throwable e) { + MethodCallCounter.observe(this, "afterThrowsCheckedException", ret, e); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java new file mode 100644 index 0000000..0dcc76a --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java @@ -0,0 +1,33 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.After; +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; + +@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.StaticFinalExample") +public class StaticFinalTestHook { + + public StaticFinalTestHook(MetricsStore m) {} + + @Before(method = { + "helloPublic", + "helloPublicFinal", + "helloPublicStatic", + "helloPublicStaticFinal" + }) + public void before(String name) { + MethodCallCounter.observe(this, "before", name); + } + + @After(method = { + "helloPublic", + "helloPublicFinal", + "helloPublicStatic", + "helloPublicStaticFinal" + }) + public void after(String name) { + MethodCallCounter.observe(this, "after", name); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java new file mode 100644 index 0000000..478672d --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java @@ -0,0 +1,35 @@ +package io.promagent.internal.instrumentationtests.hooks; + +import io.promagent.annotations.Before; +import io.promagent.annotations.Hook; +import io.promagent.hookcontext.MetricsStore; +import io.promagent.internal.instrumentationtests.MethodCallCounter; +import io.promagent.internal.instrumentationtests.classes.Fruit; + +/** + * Two hooks instrumenting the same class. + */ +public class TwoHooks { + + @Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") + public static class HookOne { + + public HookOne(MetricsStore m) {} + + @Before(method = "objects") + public void before(Object o, Fruit f, Fruit.Orange x) { + MethodCallCounter.observe(this, "before", o, f, x); + } + } + + @Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") + public static class HookTwo { + + public HookTwo(MetricsStore m) {} + + @Before(method = "objects") + public void before(Object o, Fruit f, Fruit.Orange x) { + MethodCallCounter.observe(this, "before", o, f, x); + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-loader/README.md b/java_jar_agent/promagent-framework/promagent-loader/README.md new file mode 100644 index 0000000..802f4f8 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-loader/README.md @@ -0,0 +1,20 @@ +promagent-loader +---------------- + +Experimental tool for loading the promagent into an existing JVM. Usage: + +```bash +java -cp $JAVA_HOME/lib/tools.jar:/path/to/promagent-loader.jar io.promagent.loader.PromagentLoader -agent /path/to/promagent.jar -port 9300 -pid +``` + +The `promagent-loader` uses OpenJDK API and will probably not work with other Java VMs. + +With JDK 8 or earlier, the file `$JAVA_HOME/lib/tools.jar` must exist. With JDK 9 or higher, the loader runs without this external dependency, because the classes have been moved into the Java runtime. + +The `/path/to/promagent.jar` must be an absolute path, not a relative path. + +The port is the TCP port for the exporter, like when promagent is attached on JVM startup with parameter `-javaagent:/path/to/promagent.jar=port=9300`. + +The `` is the PID of the Java process that the agent should attach to. It can be found with the `jps` command. + +See [https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent](https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent) for a more portable implementation of an agent loader. diff --git a/java_jar_agent/promagent-framework/promagent-loader/pom.xml b/java_jar_agent/promagent-framework/promagent-loader/pom.xml new file mode 100644 index 0000000..1afa096 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-loader/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + + promagent-loader + promagent-loader attach promagent to a running JVM + + jar + + + + org.junit.jupiter + junit-jupiter-api + test + + + + com.sun + tools + 1.8 + system + ${java.home}/../lib/tools.jar + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + io.promagent.loader.PromagentLoader + + + + + + + diff --git a/java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java b/java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java new file mode 100644 index 0000000..4843ecf --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java @@ -0,0 +1,87 @@ +package io.promagent.loader; + +import com.sun.tools.attach.AgentLoadException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; + +public class PromagentLoader { + + public static void main(String[] args) throws Exception { + int pid = getIntArg(args, "-pid"); + int port = getIntArg(args, "-port"); + String agentJar = getStringArg(args, "-agent"); + PromagentLoader.loadPromagent(agentJar, pid, port); + } + + private static void loadPromagent(String agentJar, int pid, int port) throws Exception { + VirtualMachineDescriptor vmd = findVirtualMachine(Integer.toString(pid)); + if (vmd == null) { + System.err.println("No Java process found with PID " + pid); + System.exit(-1); + } + VirtualMachine vm = null; + try { + vm = VirtualMachine.attach(vmd); + vm.loadAgent(agentJar, "port=" + port); + } catch (AgentLoadException e) { + System.err.println("Failed to attach agent: " + getMessage(e)); + } finally { + if (vm != null) { + vm.detach(); + } + } + } + + private static VirtualMachineDescriptor findVirtualMachine(String pid) { + for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { + if (vmd.id().equalsIgnoreCase(pid)) { + return vmd; + } + } + return null; + } + + private static String getMessage(AgentLoadException e) { + switch (e.getMessage()) { + case "-4": + return "Insuffient memory"; + case "100": + return "Agent JAR not found or no Agent-Class attribute"; + case "101": + return "Unable to add JAR file to system class path"; + case "102": + return "Agent JAR loaded but agent failed to initialize"; + default: + return e.getMessage(); + } + } + + private static int getIntArg(String[] args, String option) { + String stringArg = getStringArg(args, option); + try { + return Integer.parseInt(stringArg); + } catch (NumberFormatException e) { + System.err.println(option + " " + stringArg + ": invalid argument"); + System.exit(-1); + return 0; // will never happen + } + } + + private static String getStringArg(String[] args, String option) { + for (int pos : new int[]{0, 2, 4}) { + if (args.length < pos + 2) { + printUsageAndExit(); + } + if (option.equals(args[pos])) { + return args[pos+1]; + } + } + printUsageAndExit(); + return null; // will never happen + } + + private static void printUsageAndExit() { + System.err.println("Usage: java -cp $JAVA_HOME/lib/tools.jar:/path/to/promagent-loader.jar io.promagent.loader.PromagentLoader -agent /path/to/promagent.jar -port 9300 -pid "); + System.exit(-1); + } +} diff --git a/java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml b/java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml new file mode 100644 index 0000000..b428f60 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml @@ -0,0 +1,73 @@ + + 4.0.0 + + + io.promagent + promagent-framework + 1.0-SNAPSHOT + + + promagent-maven-plugin + promagent-maven-plugin + + maven-plugin + + + + + io.promagent + promagent-internal + 1.0-SNAPSHOT + runtime + + + + io.promagent + promagent-agent + 1.0-SNAPSHOT + runtime + + + + io.promagent + promagent-api + 1.0-SNAPSHOT + runtime + + + + org.apache.maven + maven-plugin-api + 3.6.0 + provided + + + + org.apache.maven + maven-core + 3.6.0 + provided + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + provided + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + promagent + + + + + diff --git a/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java b/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java new file mode 100644 index 0000000..42497d5 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java @@ -0,0 +1,177 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.plugin; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import static java.util.stream.Collectors.toList; + +/** + * Hard-coded list of JAR files that must go into the agent.jar. + * Versions are resolved through Maven. + * If the user adds a duplicate JAR with a different version, the plugin fails. + */ +class AgentDependencies { + + private final String pluginGroupId; + private final List dependencies; + + /** + * Hard-coded dependencies without versions (versions are resolved dynamically). + */ + private static class ExpectedDependency { + + final String groupId; + final String artifactId; + + private ExpectedDependency(String groupId, String artifactId) { + this.groupId = groupId; + this.artifactId = artifactId; + } + } + + private AgentDependencies(String pluginGroupId, List dependencies) { + this.pluginGroupId = pluginGroupId; + this.dependencies = dependencies; + } + + static AgentDependencies init(PluginDescriptor pluginDescriptor) throws MojoExecutionException { + + String pluginGroupId = pluginDescriptor.getGroupId(); + String pluginArtifactId = pluginDescriptor.getArtifactId(); + + List expectedDependencies = Arrays.asList( + new ExpectedDependency(pluginGroupId, "promagent-agent"), + new ExpectedDependency(pluginGroupId, "promagent-internal"), + new ExpectedDependency(pluginGroupId, "promagent-api"), + new ExpectedDependency("io.prometheus", "simpleclient_common"), + new ExpectedDependency("io.prometheus", "simpleclient"), + new ExpectedDependency("net.bytebuddy", "byte-buddy"), + new ExpectedDependency("commons-io", "commons-io") + ); + + List actualDependencies = resolveVersions(pluginDescriptor, pluginArtifactId, expectedDependencies); + failUnlessComplete(actualDependencies, expectedDependencies, pluginArtifactId); + return new AgentDependencies(pluginGroupId, actualDependencies); + } + + /** + * Artifact for the promagent-agent module. + */ + Artifact getAgentArtifact() { + return dependencies.stream() + .filter(isAgent()) + .findFirst() + .get(); // We know it's present. + } + + /** + * Artifact for all other runtime dependencies except promagent-agent. + */ + List getDependencies() { + return dependencies.stream() + .filter(isAgent().negate()) + .collect(toList()); + } + + void assertNoConflict(Artifact artifact) throws MojoExecutionException { + Optional builtInVersion = dependencies.stream() + .filter(dependency -> dependency.getGroupId().equals(artifact.getGroupId())) + .filter(dependency -> dependency.getArtifactId().equals(artifact.getArtifactId())) + .map(Artifact::getVersion) + .findFirst(); + if (builtInVersion.isPresent() && ! builtInVersion.get().equals(artifact.getVersion())) { + String artifactName = artifact.getGroupId() + ":" + artifact.getArtifactId(); + throw new MojoExecutionException("Conflicting dependencies: Your project includes " + artifactName + + " version " + artifact.getVersion() + " but the promagent-maven-plugin is built with version " + builtInVersion.get()); + } + } + + private static List resolveVersions(PluginDescriptor pluginDescriptor, String pluginArtifactId, List expectedDependencies) throws MojoExecutionException { + List actualDependencies = new ArrayList<>(); + for (Artifact artifact : pluginDescriptor.getArtifacts()) { + if (! isExpected(artifact, expectedDependencies)) { + continue; + } + if (isKnown(artifact, actualDependencies)) { + continue; + } + failOnVersionConflict(artifact, actualDependencies, pluginArtifactId); + actualDependencies.add(artifact); + } + return actualDependencies; + } + + private Predicate isAgent() { + return artifact -> artifact.getGroupId().equals(pluginGroupId) && artifact.getArtifactId().equals("promagent-agent"); + } + + + private static Predicate expectedDependencyMatcher(Artifact artifact) { + return expectedDependency -> expectedDependency.groupId.equals(artifact.getGroupId()) && + expectedDependency.artifactId.equals(artifact.getArtifactId()); + } + + private static Predicate expectedDependencyMatcher(ExpectedDependency expectedDependency) { + return artifact -> expectedDependencyMatcher(artifact).test(expectedDependency); + } + + private static Predicate artifactMatcherWithoutVersion(Artifact artifact) { + return other -> artifact.getGroupId().equals(other.getGroupId()) && + artifact.getArtifactId().equals(other.getArtifactId()); + } + + private static Predicate artifactMatcherWithVersion(Artifact artifact) { + return other -> artifactMatcherWithoutVersion(artifact).test(other) && + artifact.getVersion().equals(other.getVersion()); + } + + private static boolean isExpected(Artifact artifact, List expectedDependencies) { + return expectedDependencies.stream().anyMatch(expectedDependencyMatcher(artifact)); + } + + private static boolean isKnown(Artifact artifact, List knownArtifacts) { + return knownArtifacts.stream().anyMatch(artifactMatcherWithVersion(artifact)); + } + + private static void failOnVersionConflict(Artifact artifact, List knownArtifacts, String pluginArtifactId) throws MojoExecutionException { + Optional conflictingVersion = knownArtifacts.stream() + .filter(artifactMatcherWithoutVersion(artifact)) + .filter(artifactMatcherWithVersion(artifact).negate()) // same version -> not conflicting + .findFirst() + .map(Artifact::getVersion); + if (conflictingVersion.isPresent()) { + String artifactName = artifact.getGroupId() + artifact.getArtifactId(); + throw new MojoExecutionException("version conflict in " + pluginArtifactId + ": " + artifactName + " found in version " + artifact.getVersion() + " and version " + conflictingVersion.get()); + } + } + + private static void failUnlessComplete(List actualDependencies, List expectedDependencies, String pluginArtifactId) throws MojoExecutionException { + for (ExpectedDependency expected : expectedDependencies) { + if (actualDependencies.stream().noneMatch(expectedDependencyMatcher(expected))) { + String dependencyName = expected.groupId + ":" + expected.artifactId; + throw new MojoExecutionException("Plugin dependency " + dependencyName + " missing. This is a bug in " + pluginArtifactId + "."); + } + } + } +} diff --git a/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java b/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java new file mode 100644 index 0000000..a124ca6 --- /dev/null +++ b/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java @@ -0,0 +1,135 @@ +// Copyright 2017 The Promagent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.promagent.plugin; + +import org.apache.maven.plugin.MojoExecutionException; +import org.codehaus.plexus.util.IOUtil; + +import java.io.*; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +class AgentJar implements AutoCloseable { + + private final String jarName; + private final JarOutputStream jarOutputStream; + private final Set content = new HashSet<>(); + + enum Directory { + SHARED_JARS("shared-jars/"), // Directory entries in JAR file must end in '/' + PER_DEPLOYMENT_JARS("per-deployment-jars/"); + + private final String name; + + Directory(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + private AgentJar(String jarName, JarOutputStream jarOutputStream) { + this.jarName = jarName; + this.jarOutputStream = jarOutputStream; + } + + static AgentJar create(File jarFile) throws MojoExecutionException { + try { + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile))); + return new AgentJar(jarFile.getName(), jarOutputStream); + } catch (IOException e) { + throw new MojoExecutionException("Error creating " + jarFile.getName() + ": " + e.getMessage(), e); + } + } + + void addFile(File srcFile, String targetFileName, Directory targetDir) throws MojoExecutionException { + String destPath = targetDir.getName() + targetFileName; + if (content.contains(destPath)) { + return; + } + makeDirsRecursively(destPath); + content.add(destPath); + try (InputStream in = new FileInputStream(srcFile)) { + jarOutputStream.putNextEntry(new JarEntry(destPath)); + IOUtil.copy(in, jarOutputStream); + } catch (IOException e) { + throw new MojoExecutionException("Error adding " + srcFile.getName() + " to target JAR: " + e.getMessage(), e); + } + } + + void addFile(File srcFile, Directory targetDir) throws MojoExecutionException { + addFile(srcFile, srcFile.getName(), targetDir); + } + + void extractJar(File jar, ManifestTransformer manifestTransformer) throws MojoExecutionException { + try (JarFile jarFile = new JarFile(jar)) { + for (Enumeration jarEntries = jarFile.entries(); jarEntries.hasMoreElements(); ) { + JarEntry jarEntry = jarEntries.nextElement(); + if (manifestTransformer.canTransform(jarEntry)) { + jarEntry = manifestTransformer.transform(jarEntry); + } + if (!jarEntry.isDirectory() && !content.contains(jarEntry.getName())) { + content.add(jarEntry.getName()); + makeDirsRecursively(jarEntry.getName()); + try (InputStream in = getInputStream(jarEntry, jarFile, manifestTransformer)) { + jarOutputStream.putNextEntry(jarEntry); + IOUtil.copy(in, jarOutputStream); + } + } + } + } catch (IOException e) { + throw new MojoExecutionException("Error adding " + jar.getName() + " to target JAR: " + e.getMessage(), e); + } + } + + private InputStream getInputStream(JarEntry jarEntry, JarFile jarFile, ManifestTransformer manifestTransformer) throws IOException, MojoExecutionException { + InputStream in = jarFile.getInputStream(jarEntry); + if (manifestTransformer.canTransform(jarEntry)) { + in = manifestTransformer.transform(in); + } + return in; + } + + private void makeDirsRecursively(String path) throws MojoExecutionException { + String[] parts = path.split("/+"); + String segment = ""; + for (int i=0; i Date: Fri, 15 Feb 2019 14:39:13 +0530 Subject: [PATCH 21/38] Adding Java agent loader --- agentloader/pom.xml | 17 +++++++++++++++++ agentloader/src/main/java/Main.java | 27 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 agentloader/pom.xml create mode 100644 agentloader/src/main/java/Main.java diff --git a/agentloader/pom.xml b/agentloader/pom.xml new file mode 100644 index 0000000..78539f4 --- /dev/null +++ b/agentloader/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + com.stackroute + agent-loader + 1.0-SNAPSHOT + + + 1.6 + 1.6 + + + + \ No newline at end of file diff --git a/agentloader/src/main/java/Main.java b/agentloader/src/main/java/Main.java new file mode 100644 index 0000000..3d999ca --- /dev/null +++ b/agentloader/src/main/java/Main.java @@ -0,0 +1,27 @@ +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; + +import java.util.List; + +public class Main { + public static void main(String[] args){ + + List vmList = VirtualMachine.list(); + + int port = Integer.valueOf(args[1]); + + for(VirtualMachineDescriptor vm : vmList){ + System.out.println("name: " + vm.displayName() + " id :" + vm.id()); + try{ + VirtualMachine vm0 = VirtualMachine.attach(vm.id()); + // load agent, agnet class agentmain will be invoked. + vm0.loadAgent(args[0], "port="+ Integer.toString(port++)); + System.out.println("Load agent done."); + vm0.detach(); + }catch(Exception e) { + System.out.println("exception : " + e.getMessage()); + } + } + + } +} From 6efa368f7d925b2894af06c6301c3033df8c86c0 Mon Sep 17 00:00:00 2001 From: swethadevid Date: Fri, 15 Feb 2019 14:47:32 +0530 Subject: [PATCH 22/38] Added Monitoring service --- monitoring service/.gitignore | 25 ++++++ monitoring service/Dockerfile | 4 + monitoring service/pom.xml | 85 +++++++++++++++++++ .../KafkaConfig/KafkaConfiguration.java | 39 +++++++++ .../KafkaListnerService/KafkaListenerSer.java | 48 +++++++++++ .../monitoring/service/Model/Monitor.java | 83 ++++++++++++++++++ .../MonitoringController/MonController.java | 47 ++++++++++ .../service/MonitoringServiceApplication.java | 16 ++++ .../src/main/resources/application.properties | 1 + .../MonitoringServiceApplicationTests.java | 17 ++++ 10 files changed, 365 insertions(+) create mode 100644 monitoring service/.gitignore create mode 100644 monitoring service/Dockerfile create mode 100644 monitoring service/pom.xml create mode 100644 monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaConfig/KafkaConfiguration.java create mode 100644 monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaListnerService/KafkaListenerSer.java create mode 100644 monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/Model/Monitor.java create mode 100644 monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringController/MonController.java create mode 100644 monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplication.java create mode 100644 monitoring service/src/main/resources/application.properties create mode 100644 monitoring service/src/test/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplicationTests.java diff --git a/monitoring service/.gitignore b/monitoring service/.gitignore new file mode 100644 index 0000000..c456c4a --- /dev/null +++ b/monitoring service/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +/build/ diff --git a/monitoring service/Dockerfile b/monitoring service/Dockerfile new file mode 100644 index 0000000..e717186 --- /dev/null +++ b/monitoring service/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:11.0-oracle +ADD ./target/monitoring-service-0.0.1-SNAPSHOT.jar /usr/src/monitoring-service-0.0.1-SNAPSHOT.jar +WORKDIR usr/src +ENTRYPOINT ["java","-jar","monitoring-service-0.0.1-SNAPSHOT.jar"] diff --git a/monitoring service/pom.xml b/monitoring service/pom.xml new file mode 100644 index 0000000..c9406a9 --- /dev/null +++ b/monitoring service/pom.xml @@ -0,0 +1,85 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.syscop.monitoringService + monitoring-service + 0.0.1-SNAPSHOT + monitoring service + monitoring serice + + + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.kafka + spring-kafka + + + + org.codehaus.jackson + jackson-xc + 1.9.11 + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework + spring-context-support + + + + org.quartz-scheduler + quartz + ${quartz.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaConfig/KafkaConfiguration.java b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaConfig/KafkaConfiguration.java new file mode 100644 index 0000000..9eb7960 --- /dev/null +++ b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaConfig/KafkaConfiguration.java @@ -0,0 +1,39 @@ +package com.syscop.monitoringService.monitoring.service.KafkaConfig; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +import java.util.HashMap; +import java.util.Map; + + + @EnableKafka + @Configuration + public class KafkaConfiguration { + @Bean + public ConsumerFactory consumerFactory(){ + Map config = new HashMap<> (); + + config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"172.23.239.85:9092"); + config.put(ConsumerConfig.GROUP_ID_CONFIG,"group_id2"); + config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringDeserializer.class); + + return new DefaultKafkaConsumerFactory<> (config); + } + + @Bean + public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory(){ + ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory(); + factory.setConsumerFactory(consumerFactory()); + return factory; + } + } + + diff --git a/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaListnerService/KafkaListenerSer.java b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaListnerService/KafkaListenerSer.java new file mode 100644 index 0000000..885810b --- /dev/null +++ b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/KafkaListnerService/KafkaListenerSer.java @@ -0,0 +1,48 @@ +package com.syscop.monitoringService.monitoring.service.KafkaListnerService; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.syscop.monitoringService.monitoring.service.Model.Monitor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Service; + +@Service +public class KafkaListenerSer { + + @Autowired + private Monitor monitor; + + @KafkaListener(topics = "Kafka_Example_Test_Final", groupId = "group_id2") + public void consume(String message) throws JsonProcessingException { + System.out.println("Consumed msg : " + message); + + + String[] strMessage = message.split(","); + + monitor.setContainerId(strMessage[1].split(":")[1].replace("\"","")); + monitor.setContainerName(strMessage[1].split(":")[1].replace("\"","")); + monitor.setCpu(strMessage[4].split(":")[1].replace("\"","")); + monitor.setMem(strMessage[2].split(":")[1].replace("\"","")); + monitor.setNetIO(strMessage[3].split(":")[1].replace("\"","")); + monitor.setBlockIO (strMessage[5].split(":")[1].replace("\"","")); + monitor.setpId (strMessage[6].split(":")[1].replace("\"","")); + + + System.out.println(monitor.toString ()); + + ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); + String json = ow.writeValueAsString(strMessage); +// +// userRepository.save(user); +// System.out.println("New Message"); + + // System.out.println("Consumed msg : " + message); + + } + + +} + +//{"id":"5c62ba2be20e7a15998003d3","username":"phone1","password":"123phone","phoneNumber":"9090909099","email":"swetha96devi@gmail.com","company":"BOEING","dob":"29/2/1996","roles":[]} diff --git a/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/Model/Monitor.java b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/Model/Monitor.java new file mode 100644 index 0000000..0f7e536 --- /dev/null +++ b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/Model/Monitor.java @@ -0,0 +1,83 @@ +package com.syscop.monitoringService.monitoring.service.Model; + +import org.springframework.stereotype.Component; + +import java.util.List; + + +@Component +public class Monitor { + + private String containerId; + + public String getContainerId() { + return containerId; + } + + public void setContainerId(String containerId) { + this.containerId = containerId; + } + + public String getContainerName() { + return containerName; + } + + public void setContainerName(String containerName) { + this.containerName = containerName; + } + + public String getCpu() { + return cpu; + } + + public void setCpu(String cpu) { + this.cpu = cpu; + } + + public String getMem() { + return mem; + } + + public void setMem(String mem) { + this.mem = mem; + } + + public String getNetIO() { + return netIO; + } + + public void setNetIO(String netIO) { + this.netIO = netIO; + } + + public String getBlockIO() { + return blockIO; + } + + public void setBlockIO(String blockIO) { + this.blockIO = blockIO; + } + + public String getpId() { + return pId; + } + + public void setpId(String pId) { + this.pId = pId; + } + + private String containerName; + private String cpu; + private String mem; + private String netIO; + private String blockIO; + private String pId; + + public Monitor() + { + + } + + + +} diff --git a/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringController/MonController.java b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringController/MonController.java new file mode 100644 index 0000000..07897e2 --- /dev/null +++ b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringController/MonController.java @@ -0,0 +1,47 @@ +package com.syscop.monitoringService.monitoring.service.MonitoringController; + + +import com.syscop.monitoringService.monitoring.service.KafkaListnerService.KafkaListenerSer; +import com.syscop.monitoringService.monitoring.service.Model.Monitor; +import org.apache.tomcat.util.json.JSONParser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + + +@RestController +@RequestMapping("api/v1/") +public class MonController { + + @Autowired + private KafkaListenerSer kafkaListenerSer; + + + private Monitor monitor; + + @Autowired + public MonController(Monitor monitor) { + this.monitor = monitor; + } + + @GetMapping("data") + public ResponseEntity getMonitorData(){ + + System.out.println (monitor); + //List allObjects= kafkaListenerSer.toString (); + //return new ResponseEntity<>(monitor.toString (), HttpStatus.OK); + return new ResponseEntity<>(monitor, HttpStatus.OK); + + } + + + + + +} diff --git a/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplication.java b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplication.java new file mode 100644 index 0000000..071933f --- /dev/null +++ b/monitoring service/src/main/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplication.java @@ -0,0 +1,16 @@ +package com.syscop.monitoringService.monitoring.service; + +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class MonitoringServiceApplication { + + public static void main(String[] args) { + SpringApplication.run(MonitoringServiceApplication.class,args); + } + +} + diff --git a/monitoring service/src/main/resources/application.properties b/monitoring service/src/main/resources/application.properties new file mode 100644 index 0000000..df73a30 --- /dev/null +++ b/monitoring service/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=8018 diff --git a/monitoring service/src/test/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplicationTests.java b/monitoring service/src/test/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplicationTests.java new file mode 100644 index 0000000..ad5eb7a --- /dev/null +++ b/monitoring service/src/test/java/com/syscop/monitoringService/monitoring/service/MonitoringServiceApplicationTests.java @@ -0,0 +1,17 @@ +//package com.syscop.monitoringService.monitoring.service; +// +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.test.context.junit4.SpringRunner; +// +//@RunWith(SpringRunner.class) +//@SpringBootTest +//public class MonitoringServiceApplicationTests { +// +// @Test +// public void contextLoads() { +// } +// +//} +// From 310e32531d17b27fd67958fc48f0ccde6984f378 Mon Sep 17 00:00:00 2001 From: anjalikali Date: Fri, 15 Feb 2019 14:54:15 +0530 Subject: [PATCH 23/38] adding datacollector --- dataCollectorFetcher | 1 + 1 file changed, 1 insertion(+) create mode 160000 dataCollectorFetcher diff --git a/dataCollectorFetcher b/dataCollectorFetcher new file mode 160000 index 0000000..b3abb4b --- /dev/null +++ b/dataCollectorFetcher @@ -0,0 +1 @@ +Subproject commit b3abb4b465df5a8775fc34153327526e2454ff6e From 633ba10864cc76a17153b757c80e6b4890e13cb2 Mon Sep 17 00:00:00 2001 From: anu Date: Fri, 15 Feb 2019 16:02:25 +0530 Subject: [PATCH 24/38] agent --- .../Promagent/pom-with-docker-tests.xml | 386 ------------------ java_jar_agent/Promagent/pom.xml | 76 ---- .../io/promagent/collectors/JmxCollector.java | 35 -- .../java/io/promagent/hooks/HttpContext.java | 33 -- .../java/io/promagent/hooks/JdbcHook.java | 128 ------ .../java/io/promagent/hooks/ServletHook.java | 105 ----- .../test/java/io/promagent/it/SpringIT.java | 68 --- .../test/java/io/promagent/it/WildflyIT.java | 67 --- java_jar_agent/promagent-framework/pom.xml | 85 ---- .../promagent-agent/pom.xml | 42 -- .../io/promagent/agent/ClassLoaderCache.java | 82 ---- .../java/io/promagent/agent/JarFiles.java | 141 ------- .../agent/PerDeploymentClassLoader.java | 57 --- .../java/io/promagent/agent/Promagent.java | 34 -- .../java/io/promagent/agent/JarFilesTest.java | 59 --- .../promagent-framework/promagent-api/pom.xml | 21 - .../java/io/promagent/annotations/After.java | 31 -- .../java/io/promagent/annotations/Before.java | 31 -- .../java/io/promagent/annotations/Hook.java | 45 -- .../io/promagent/annotations/Returned.java | 45 -- .../java/io/promagent/annotations/Thrown.java | 47 --- .../io/promagent/hookcontext/MetricDef.java | 52 --- .../promagent/hookcontext/MetricsStore.java | 54 --- .../promagent-exporter/pom.xml | 31 -- .../exporter/PromagentExporterServlet.java | 43 -- .../promagent-internal/pom.xml | 69 ---- .../io/promagent/internal/BuiltInServer.java | 69 ---- .../java/io/promagent/internal/Delegator.java | 276 ------------- .../io/promagent/internal/HookException.java | 26 -- .../io/promagent/internal/HookInstance.java | 20 - .../io/promagent/internal/HookMetadata.java | 141 ------- .../internal/HookMetadataParser.java | 335 --------------- .../java/io/promagent/internal/Promagent.java | 155 ------- .../promagent/internal/PromagentAdvice.java | 78 ---- .../io/promagent/internal/jmx/Exporter.java | 41 -- .../promagent/internal/jmx/ExporterMBean.java | 19 - .../io/promagent/internal/jmx/Metric.java | 47 --- .../promagent/internal/jmx/MetricMBean.java | 28 -- .../jmx/PromagentCollectorRegistry.java | 66 --- .../internal/HookMetadataParserTest.java | 114 ------ .../promagent/internal/HookMetadataTest.java | 48 --- .../instrumentationtests/Instrumentor.java | 38 -- .../instrumentationtests/LifecycleTest.java | 112 ----- .../MethodCallCounter.java | 66 --- .../ParameterTypesTest.java | 238 ----------- .../ReturnedAndThrownTest.java | 120 ------ .../instrumentationtests/StaticFinalTest.java | 66 --- .../internal/instrumentationtests/Util.java | 39 -- .../instrumentationtests/classes/Fruit.java | 8 - .../classes/IParameterTypesExample.java | 39 -- .../classes/IReturnedAndThrownExample.java | 23 -- .../classes/ParameterTypesExample.java | 51 --- .../classes/ReturnedAndThrownExample.java | 42 -- .../classes/StaticFinalExample.java | 20 - .../hooks/LifecycleHookSkipFalse.java | 32 -- .../hooks/LifecycleHookSkipTrue.java | 34 -- .../hooks/OnlyAfterHook.java | 21 - .../hooks/OnlyBeforeHook.java | 21 - .../hooks/ParameterTypesHook.java | 167 -------- .../hooks/ReturnedAndThrownHook.java | 84 ---- .../hooks/StaticFinalTestHook.java | 33 -- .../instrumentationtests/hooks/TwoHooks.java | 35 -- .../promagent-loader/README.md | 20 - .../promagent-loader/pom.xml | 47 --- .../io/promagent/loader/PromagentLoader.java | 87 ---- .../promagent-maven-plugin/pom.xml | 73 ---- .../promagent/plugin/AgentDependencies.java | 177 -------- .../java/io/promagent/plugin/AgentJar.java | 135 ------ .../io/promagent/plugin/JarFileNames.java | 63 --- .../promagent/plugin/ManifestTransformer.java | 64 --- .../io/promagent/plugin/PromagentMojo.java | 62 --- war_agent/pom-with-docker-tests.xml | 386 ------------------ war_agent/pom.xml | 76 ---- .../io/promagent/collectors/JmxCollector.java | 35 -- .../java/io/promagent/hooks/HttpContext.java | 33 -- .../java/io/promagent/hooks/JdbcHook.java | 128 ------ .../java/io/promagent/hooks/ServletHook.java | 105 ----- .../test/java/io/promagent/it/SpringIT.java | 68 --- .../test/java/io/promagent/it/WildflyIT.java | 67 --- 79 files changed, 6245 deletions(-) delete mode 100644 java_jar_agent/Promagent/pom-with-docker-tests.xml delete mode 100644 java_jar_agent/Promagent/pom.xml delete mode 100644 java_jar_agent/Promagent/src/main/java/io/promagent/collectors/JmxCollector.java delete mode 100644 java_jar_agent/Promagent/src/main/java/io/promagent/hooks/HttpContext.java delete mode 100644 java_jar_agent/Promagent/src/main/java/io/promagent/hooks/JdbcHook.java delete mode 100644 java_jar_agent/Promagent/src/main/java/io/promagent/hooks/ServletHook.java delete mode 100644 java_jar_agent/Promagent/src/test/java/io/promagent/it/SpringIT.java delete mode 100644 java_jar_agent/Promagent/src/test/java/io/promagent/it/WildflyIT.java delete mode 100644 java_jar_agent/promagent-framework/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-agent/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java delete mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/JarFiles.java delete mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java delete mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java delete mode 100644 java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java delete mode 100644 java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java delete mode 100644 java_jar_agent/promagent-framework/promagent-exporter/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java delete mode 100644 java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java delete mode 100644 java_jar_agent/promagent-framework/promagent-loader/README.md delete mode 100644 java_jar_agent/promagent-framework/promagent-loader/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java delete mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml delete mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java delete mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java delete mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/JarFileNames.java delete mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/ManifestTransformer.java delete mode 100644 java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/PromagentMojo.java delete mode 100644 war_agent/pom-with-docker-tests.xml delete mode 100644 war_agent/pom.xml delete mode 100644 war_agent/src/main/java/io/promagent/collectors/JmxCollector.java delete mode 100644 war_agent/src/main/java/io/promagent/hooks/HttpContext.java delete mode 100644 war_agent/src/main/java/io/promagent/hooks/JdbcHook.java delete mode 100644 war_agent/src/main/java/io/promagent/hooks/ServletHook.java delete mode 100644 war_agent/src/test/java/io/promagent/it/SpringIT.java delete mode 100644 war_agent/src/test/java/io/promagent/it/WildflyIT.java diff --git a/java_jar_agent/Promagent/pom-with-docker-tests.xml b/java_jar_agent/Promagent/pom-with-docker-tests.xml deleted file mode 100644 index 99be21a..0000000 --- a/java_jar_agent/Promagent/pom-with-docker-tests.xml +++ /dev/null @@ -1,386 +0,0 @@ - - 4.0.0 - - io.promagent - promagent-example - 1.0-SNAPSHOT - - promagent example - - jar - - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.0-SNAPSHOT - - - - - - io.promagent - promagent-api - ${promagent.framework.version} - provided - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - com.squareup.okhttp3 - okhttp - 3.11.0 - test - - - - - - promagent - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.1 - - - - - - io.promagent - promagent-maven-plugin - ${promagent.framework.version} - - - promagent - package - - build - - - - - - - - io.fabric8 - docker-maven-plugin - 0.27.2 - - true - - - - - ${project.artifactId}/oracle-jdk-8 - - ubuntu:17.10 - /opt - - apt-get update ; apt-get upgrade -y - apt-get install -y curl git neovim sudo - curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz - tar xfz jdk-8u192-linux-x64.tar.gz - curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz - tar xfz apache-maven-3.6.0-bin.tar.gz - - echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ - echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ - echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ - echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; - - adduser --disabled-password --gecos '' promagent - echo 'promagent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers - - promagent - - /opt/jdk1.8.0_192 - /opt/apache-maven-3.6.0 - /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH - - - - - - - ${project.artifactId}/wildfly-kitchensink - - ${project.artifactId}/oracle-jdk-8 - /home/promagent - - curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz - tar xfz wildfly-10.1.0.Final.tar.gz - rm wildfly-10.1.0.Final.tar.gz - git clone https://github.com/wildfly/quickstart.git - cd quickstart; git checkout 10.x - cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package - mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments - - mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments - rm -rf ./quickstart ; rm -rf ./.m2 - - - - - - - ${project.artifactId}/wildfly-kitchensink-promagent - - ${project.artifactId}/wildfly-kitchensink - - - echo '#!/bin/bash' >> run.sh ; \ - echo >> run.sh ; \ - echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ - echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ - echo 'export JAVA_OPTS="' >> run.sh ; \ - echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ - echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ - echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.promagent.agent' >> run.sh ; \ - echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ - echo ' -javaagent:../promagent.jar=port=9300' >> run.sh ; \ - echo '"' >> run.sh ; \ - echo >> run.sh ; \ - echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ - chmod 755 run.sh ; - - - - /home/promagent - promagent:promagent:promagent - - - - target/promagent.jar - / - - - - - - - - - - ${wildfly.port}:8080 - ${promagent.port}:9300 - - - WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms - - - ./run.sh - - true - [Wildfly] - - - - - - - ${project.artifactId}/spring - - ${project.artifactId}/oracle-jdk-8 - /home/promagent - - git clone https://github.com/spring-guides/gs-accessing-data-rest.git - cd gs-accessing-data-rest/complete; mvn --batch-mode package - mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . - rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 - - - - - - - ${project.artifactId}/spring-promagent - - ${project.artifactId}/spring - - - echo '#!/bin/bash' >> run.sh ; \ - echo >> run.sh ; \ - echo 'java -javaagent:promagent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ - chmod 755 run.sh ; - - - - /home/promagent - promagent:promagent:promagent - - - - target/promagent.jar - / - - - - - - - - - - ${spring.port}:8080 - ${promagent.port}:9300 - - - Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) - - - ./run.sh - - true - [Spring Boot] - - - - - - - - - start - pre-integration-test - - build - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - none - - skip-docker-tests - - - - wildfly - - - ${project.artifactId}/oracle-jdk-8, - ${project.artifactId}/wildfly-kitchensink, - ${project.artifactId}/wildfly-kitchensink-promagent - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/WildflyIT.java - - - - - http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink - http://${docker.host.address}:${promagent.port} - - - - - integration-tests - - integration-test - verify - - - - - - - - - spring - - - ${project.artifactId}/oracle-jdk-8, - ${project.artifactId}/spring, - ${project.artifactId}/spring-promagent - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/SpringIT.java - - - - - http://${docker.host.address}:${spring.port} - http://${docker.host.address}:${promagent.port} - - - - - integration-tests - - integration-test - verify - - - - - - - - - diff --git a/java_jar_agent/Promagent/pom.xml b/java_jar_agent/Promagent/pom.xml deleted file mode 100644 index 0ef05e6..0000000 --- a/java_jar_agent/Promagent/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - 4.0.0 - - io.promagent - promagent-example - 1.0-SNAPSHOT - - promagent example - - jar - - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.0-SNAPSHOT - - - - - - io.promagent - promagent-api - ${promagent.framework.version} - provided - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - com.squareup.okhttp3 - okhttp - 3.11.0 - test - - - - - - promagent - - - io.promagent - promagent-maven-plugin - ${promagent.framework.version} - - - promagent - package - - build - - - - - - - - diff --git a/java_jar_agent/Promagent/src/main/java/io/promagent/collectors/JmxCollector.java b/java_jar_agent/Promagent/src/main/java/io/promagent/collectors/JmxCollector.java deleted file mode 100644 index fa31f86..0000000 --- a/java_jar_agent/Promagent/src/main/java/io/promagent/collectors/JmxCollector.java +++ /dev/null @@ -1,35 +0,0 @@ - -package io.promagent.collectors; - -import com.sun.management.OperatingSystemMXBean; -import io.prometheus.client.Collector; - -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) -// TODO -- This class is not loaded by default, see commented-out lines in io.promagent.internal.Promagent.premain() -// See JmxCollector in jmx_exporter -public class JmxCollector extends Collector implements Collector.Describable { - - @Override - public List collect() { - List result = new ArrayList<>(); - result.add(collectOperatingSystemMetrics()); - return Collections.unmodifiableList(result); - } - - @Override - public List describe() { - return new ArrayList<>(); - } - - private MetricFamilySamples collectOperatingSystemMetrics() { - OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); - return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); - } -} diff --git a/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/HttpContext.java b/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/HttpContext.java deleted file mode 100644 index 7d8dccc..0000000 --- a/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/HttpContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.promagent.hooks; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - - -/**Storing the http context in a thread to know which database request call was triggered. -*/ -class HttpContext { - - static class Key {} - - static final Key HTTP_METHOD = new Key<>(); - static final Key HTTP_PATH = new Key<>(); - - private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); - - static void put(Key key, T value) { - threadLocal.get().put(key, value); - } - - @SuppressWarnings("unchecked") - static Optional get(Key key) { - return Optional.ofNullable((T) threadLocal.get().get(key)); - } - - static void clear(Key... keys) { - for (Key key : keys) { - threadLocal.get().remove(key); - } - } -} diff --git a/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/JdbcHook.java b/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/JdbcHook.java deleted file mode 100644 index 675d449..0000000 --- a/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/JdbcHook.java +++ /dev/null @@ -1,128 +0,0 @@ - -package io.promagent.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricDef; -import io.promagent.hookcontext.MetricsStore; -import io.prometheus.client.Counter; -import io.prometheus.client.Summary; - -import java.util.concurrent.TimeUnit; - -import static io.promagent.hooks.HttpContext.HTTP_METHOD; -import static io.promagent.hooks.HttpContext.HTTP_PATH; - -@Hook(instruments = { - "java.sql.Statement", - "java.sql.Connection" -}) -public class JdbcHook { - - private final Counter sqlQueriesTotal; - private final Summary sqlQueriesDuration; - private long startTime = 0; - - public JdbcHook(MetricsStore metricsStore) { - - sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( - "sql_queries_total", - (name, registry) -> Counter.build() - .name(name) - .labelNames("method", "path", "query") - .help("Total number of sql queries.") - .register(registry) - )); - - sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( - "sql_query_duration", - (name, registry) -> Summary.build() - .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error - .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error - .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error - .name(name) - .labelNames("method", "path", "query") - .help("Duration for serving the sql queries in seconds.") - .register(registry) - )); - } - - private String stripValues(String query) { - // We want the structure of the query as labels, not the actual values. - // Therefore, we replace: - // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') - // with - // insert into Member (id, name, email, phone_number) values (...) - return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); - } - - // --- before - - @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) - public void before(String sql) { - startTime = System.nanoTime(); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, int autoGeneratedKeys) { - before(sql); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, int[] columnIndexes) { - before(sql); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, String[] columnNames) { - before(sql); - } - - @Before(method = {"prepareStatement", "prepareCall"}) - public void before(String sql, int resultSetType, int resultSetConcurrency) { - before(sql); - } - - @Before(method = {"prepareStatement", "prepareCall"}) - public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { - before(sql); - } - - // --- after - - @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) - public void after(String sql) throws Exception { - double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); - String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); - String path = HttpContext.get(HTTP_PATH).orElse("no http context"); - String query = stripValues(sql); - sqlQueriesTotal.labels(method, path, query).inc(); - sqlQueriesDuration.labels(method, path, query).observe(duration); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, int autoGeneratedKeys) throws Exception { - after(sql); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, int[] columnIndexes) throws Exception { - after(sql); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, String[] columnNames) throws Exception { - after(sql); - } - - @After(method = {"prepareStatement", "prepareCall"}) - public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { - after(sql); - } - - @After(method = {"prepareStatement", "prepareCall"}) - public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { - after(sql); - } -} diff --git a/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/ServletHook.java b/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/ServletHook.java deleted file mode 100644 index e29e5ba..0000000 --- a/java_jar_agent/Promagent/src/main/java/io/promagent/hooks/ServletHook.java +++ /dev/null @@ -1,105 +0,0 @@ - - -package io.promagent.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricDef; -import io.promagent.hookcontext.MetricsStore; -import io.prometheus.client.Counter; -import io.prometheus.client.Summary; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.concurrent.TimeUnit; - -import static io.promagent.hooks.HttpContext.HTTP_METHOD; -import static io.promagent.hooks.HttpContext.HTTP_PATH; - -@Hook(instruments = { - "javax.servlet.Servlet", - "javax.servlet.Filter" -}) -public class ServletHook { - - private final Counter httpRequestsTotal; - private final Summary httpRequestsDuration; - private long startTime = 0; - - public ServletHook(MetricsStore metricsStore) { - - httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( - "http_requests_total",https://github.com/asaini94/war_agent - (name, registry) -> Counter.build() - .name(name) - .labelNames("method", "path", "status") - .help("Total number of http requests.") - .register(registry) - )); - - httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( - "http_request_duration", - (name, registry) -> Summary.build() - .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error - .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error - .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error - .name(name) - .labelNames("method", "path", "status") - .help("Duration for serving the http requests in seconds.") - .register(registry) - )); - } - - private String stripPathParameters(String path) { - - // The URL path may include path parameters. - // For example, REST URLs for querying an item might look like this: - // - // /item/1 - // /item/2 - // /item/3 - // etc. - // - // /item/{id} - // - // This method replaces path parameters with placeholders. It is application specific and - // should be adapted depending on the actual paths in an application. - // For the demo, we just replace all numbers with {id}. - - return path - .replaceAll("/[0-9]+", "/{id}") - .replaceAll("/;jsessionid=\\w*", "") - .replaceAll("/$", "") - .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." - } - - @Before(method = {"service", "doFilter"}) - public void before(ServletRequest request, ServletResponse response) { - if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { - HttpServletRequest req = (HttpServletRequest) request; - HttpContext.put(HTTP_METHOD, req.getMethod()); - HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); - startTime = System.nanoTime(); - } - } - - // Return Werte und Exceptions als Parameter - @After(method = {"service", "doFilter"}) - public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { - if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { - HttpServletResponse resp = (HttpServletResponse) response; - try { - double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); - String method = HttpContext.get(HTTP_METHOD).get(); - String path = HttpContext.get(HTTP_PATH).get(); - httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); - httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); - } finally { - HttpContext.clear(HTTP_METHOD, HTTP_PATH); - } - } - } -} diff --git a/java_jar_agent/Promagent/src/test/java/io/promagent/it/SpringIT.java b/java_jar_agent/Promagent/src/test/java/io/promagent/it/SpringIT.java deleted file mode 100644 index f3ceec9..0000000 --- a/java_jar_agent/Promagent/src/test/java/io/promagent/it/SpringIT.java +++ /dev/null @@ -1,68 +0,0 @@ - - -package io.promagent.it; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.junit.jupiter.api.Test; - -public class SpringIT { - - /** - * Run some HTTP queries against a Docker container from image promagent/spring-promagent. - *

- * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. - */ - @Test - public void testSpring() throws Exception { - OkHttpClient client = new OkHttpClient(); - Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); - - // Execute two POST requests - Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); - assertEquals(201, restResponse.code()); - restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); - assertEquals(201, restResponse.code()); - - // Query Prometheus metrics from promagent - Response metricsResponse = client.newCall(metricsRequest).execute(); - String[] metricsLines = metricsResponse.body().string().split("\n"); - - String httpRequestsTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("http_requests_total")) - .filter(m -> m.contains("method=\"POST\"")) - .filter(m -> m.contains("path=\"/people\"")) - .filter(m -> m.contains("status=\"201\"")) - .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); - - assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); - - String sqlQueriesTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("sql_queries_total")) - // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: - // query="insert into person (first_name, last_name, id)" - .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) - .filter(m -> m.contains("method=\"POST\"")) - .filter(m -> m.contains("path=\"/people\"")) - .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); - - assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); - } - - private Request makePostRequest(String firstName, String lastName) { - return new Request.Builder() - .url(System.getProperty("deployment.url") + "/people") - .method("POST", RequestBody.create( - MediaType.parse("application/json"), - "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) - .build(); - } -} diff --git a/java_jar_agent/Promagent/src/test/java/io/promagent/it/WildflyIT.java b/java_jar_agent/Promagent/src/test/java/io/promagent/it/WildflyIT.java deleted file mode 100644 index 33b97a7..0000000 --- a/java_jar_agent/Promagent/src/test/java/io/promagent/it/WildflyIT.java +++ /dev/null @@ -1,67 +0,0 @@ - -package io.promagent.it; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class WildflyIT { - - /** - * Run some HTTP queries against a Docker container from image promagent/wildfly-kitchensink-promagent. - *

- * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. - */ - @Test - public void testWildfly() throws Exception { - OkHttpClient client = new OkHttpClient(); - Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); - - // Execute REST call - Response restResponse = client.newCall(restRequest).execute(); - Assertions.assertEquals(restResponse.code(), 200); - Assertions.assertTrue(restResponse.body().string().contains("John Smith")); - - Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric - assertMetrics(client, "1.0"); - - // Execute REST call again - restResponse = client.newCall(restRequest).execute(); - Assertions.assertEquals(restResponse.code(), 200); - Assertions.assertTrue(restResponse.body().string().contains("John Smith")); - - Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric - assertMetrics(client, "2.0"); - } - - private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { - - Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); - Response metricsResponse = client.newCall(metricsRequest).execute(); - String[] metricsLines = metricsResponse.body().string().split("\n"); - - String httpRequestsTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("http_requests_total")) - .filter(m -> m.contains("method=\"GET\"")) - .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) - .filter(m -> m.contains("status=\"200\"")) - .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); - - assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); - - String sqlQueriesTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("sql_queries_total")) - .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) - .filter(m -> m.contains("method=\"GET\"")) - .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) - .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); - - assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); - } -} diff --git a/java_jar_agent/promagent-framework/pom.xml b/java_jar_agent/promagent-framework/pom.xml deleted file mode 100644 index f9d4a3b..0000000 --- a/java_jar_agent/promagent-framework/pom.xml +++ /dev/null @@ -1,85 +0,0 @@ - - 4.0.0 - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - pom - promagent framework - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - UTF-8 - UTF-8 - 1.8 - 1.8 - 5.3.1 - - - - - - promagent-loader - - promagent-agent - promagent-api - promagent-internal - promagent-exporter - promagent-loader - promagent-maven-plugin - - - - - default - - true - - - promagent-agent - promagent-api - promagent-internal - promagent-exporter - promagent-maven-plugin - - - - - - - - - io.prometheus - simpleclient_common - 0.5.0 - - - org.junit.jupiter - junit-jupiter-api - ${junit5.version} - test - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - 2.22.1 - - - - - diff --git a/java_jar_agent/promagent-framework/promagent-agent/pom.xml b/java_jar_agent/promagent-framework/promagent-agent/pom.xml deleted file mode 100644 index 192ea65..0000000 --- a/java_jar_agent/promagent-framework/promagent-agent/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - - promagent-agent - promagent-agent loaded from the system class loader - - jar - - - - org.junit.jupiter - junit-jupiter-api - test - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - io.promagent.agent.Promagent - io.promagent.agent.Promagent - true - true - true - - - - - - - diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java deleted file mode 100644 index 6868f5c..0000000 --- a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/ClassLoaderCache.java +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.agent; - -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * ClassLoaderCache stores the class loaders used for loading the Promagent modules, - * i.e. promagent-hooks, promagent-annotations, promagent-internals, and their dependencies. - *

- * For the Hooks (like ServletHook or JdbcHook) there is one class loader per deployment, - * because hook classes may reference classes from the deployment, - * e.g. as parameters to the before() and after() methods. - * All other modules and their dependencies are loaded through a shared class loader. - *

- * When {@link #currentClassLoader()} is called for the first time within a class loader context, - * a new {@link PerDeploymentClassLoader} is created on the fly. - * Repeated calls in the same context yield the same {@link PerDeploymentClassLoader}. - */ -public class ClassLoaderCache { - - private static ClassLoaderCache instance; - - // TODO: The cache does not free class loaders when applications are undeployed. Maybe use WeakHashMap? - private final Map cache = new HashMap<>(); - private final URLClassLoader sharedClassLoader; // shared across multiple deployments - private final List perDeploymentJars; // one class loader for each deployment for these JARs - - private ClassLoaderCache(JarFiles jarFiles) { - sharedClassLoader = new URLClassLoader(pathsToURLs(jarFiles.getSharedJars())); - perDeploymentJars = jarFiles.getPerDeploymentJars(); - } - - public static synchronized ClassLoaderCache getInstance() { - if (instance == null) { - instance = new ClassLoaderCache(JarFiles.extract()); - } - return instance; - } - - public List getPerDeploymentJars() { - return perDeploymentJars; - } - - public synchronized ClassLoader currentClassLoader() { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if (! cache.containsKey(contextClassLoader)) { - cache.put(contextClassLoader, new PerDeploymentClassLoader(pathsToURLs(perDeploymentJars), sharedClassLoader, contextClassLoader)); - } - return cache.get(contextClassLoader); - } - - private static URL[] pathsToURLs(List paths) { - try { - URL[] result = new URL[paths.size()]; - for (int i=0; i perDeploymentJars; // classes loaded for each deployment - private final List sharedJars; // classes shared across multiple deployments - - private JarFiles(List perDeploymentJars, List sharedJars) { - this.perDeploymentJars = Collections.unmodifiableList(perDeploymentJars); - this.sharedJars = Collections.unmodifiableList(sharedJars); - } - - List getPerDeploymentJars() { - return perDeploymentJars; - } - - List getSharedJars() { - return sharedJars; - } - - /** - * Theoretically we could return a list of jar:/ URLs without extracting the JARs, - * but the URLClassLoader has a bug such that jar:/ URLs cannot be used. Therefore, we have - * to extract the JARs and return a list of file:/ URLs. - * See https://bugs.openjdk.java.net/browse/JDK-4735639 - */ - static JarFiles extract() { - List perDeploymentJars = new ArrayList<>(); - List sharedJars = new ArrayList<>(); - Path agentJar = findAgentJar(); - List extractedJars; - try { - Path tmpDir = Files.createTempDirectory("promagent-"); - tmpDir.toFile().deleteOnExit(); - extractedJars = unzip(agentJar, tmpDir, entry -> entry.getName().endsWith(".jar")); - } catch (IOException e) { - throw new RuntimeException("Failed to load promagent.jar: " + e.getMessage(), e); - } - for (Path jar : extractedJars) { - if (jar.getParent().getFileName().toString().equals("per-deployment-jars")) { - perDeploymentJars.add(jar); - } else { - sharedJars.add(jar); - } - } - return new JarFiles(perDeploymentJars, sharedJars); - } - - private static List unzip(Path jarFile, Path destDir, Predicate filter) throws IOException { - List result = new ArrayList<>(); - try (JarFile agentJar = new JarFile(jarFile.toFile())) { - Enumeration jarEntries = agentJar.entries(); - while (jarEntries.hasMoreElements()) { - JarEntry jarEntry = jarEntries.nextElement(); - if (filter.test(jarEntry)) { - Path destFile = destDir.resolve(jarEntry.getName()); - if (!destFile.getParent().toFile().exists()) { - if (!destFile.getParent().toFile().mkdirs()) { - throw new IOException("Failed to make directory: " + destFile.getParent()); - } - } - Files.copy(agentJar.getInputStream(jarEntry), destFile); - result.add(destFile); - } - } - } - return result; - } - - private static Path findAgentJar() { - CodeSource cs = Promagent.class.getProtectionDomain().getCodeSource(); - if (cs != null) { - return findAgentJarFromCodeSource(cs); - } else { - // This happens if the Promagent class is loaded from the bootstrap class loader, - // i.e. in addition to the command line argument -javaagent:/path/to/promagent.jar, - // the argument -Xbootclasspath/p:/path/to/promagent.jar is used. - return findAgentJarFromCmdline(ManagementFactory.getRuntimeMXBean().getInputArguments()); - } - } - - private static Path findAgentJarFromCodeSource(CodeSource cs) { - try { - return Paths.get(cs.getLocation().toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException("Failed to load promagent.jar from " + cs.getLocation() + ": " + e.getMessage(), e); - } - } - - static Path findAgentJarFromCmdline(List cmdlineArgs) { - Pattern p = Pattern.compile("^-javaagent:(.*promagent([^/]*).jar)(=.*)?$"); - for (String arg : cmdlineArgs) { - Matcher m = p.matcher(arg); - if (m.matches()) { - return Paths.get(m.group(1)); - } - } - throw new RuntimeException("Failed to locate promagent.jar file."); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java deleted file mode 100644 index bdce971..0000000 --- a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/PerDeploymentClassLoader.java +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.agent; - -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; - -/** - * Class loader for loading Hooks (like ServletHook or JdbcHook). - *

- * There is one instance per deployment in an application server, because hook classes may - * reference classes from the deployment, e.g. as parameters to the before() and after() methods. - *

- * However, loading shared classes like the Prometheus client library is delegated to the {@link #sharedClassLoader}, - * because the Prometheus metric registry should be accessible across all deployments within an application server. - */ -class PerDeploymentClassLoader extends URLClassLoader { - - private final URLClassLoader sharedClassLoader; // for loading the Prometheus client library - - PerDeploymentClassLoader(URL[] perDeploymentJars, URLClassLoader sharedClassLoader, ClassLoader parent) { - super(perDeploymentJars, parent); - this.sharedClassLoader = sharedClassLoader; - } - - @Override - public Class loadClass(String name) throws ClassNotFoundException { - try { - return sharedClassLoader.loadClass(name); // The Prometheus client library should all have the same initiating loader across deployments. - } catch (ClassNotFoundException e) { - return super.loadClass(name); // Hooks should have different initiating loaders if the context loader differs. - } - } - - // Called by Byte buddy to load the PromagentAdvice. - @Override - public InputStream getResourceAsStream(String name) { - InputStream result = sharedClassLoader.getResourceAsStream(name); - if (result == null) { - result = super.getResourceAsStream(name); - } - return result; - } -} diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java b/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java deleted file mode 100644 index 321aa98..0000000 --- a/java_jar_agent/promagent-framework/promagent-agent/src/main/java/io/promagent/agent/Promagent.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.agent; - -import java.lang.instrument.Instrumentation; - -/** - * We want as little dependencies as possible on the system class loader, - * so the actual agent is loaded in its own class loader and this agent delegates to it. - */ -public class Promagent { - - public static void premain(String agentArgs, Instrumentation inst) throws Exception { - ClassLoader agentClassLoader = ClassLoaderCache.getInstance().currentClassLoader(); - Class agentClass = agentClassLoader.loadClass("io.promagent.internal.Promagent"); - agentClass.getMethod("premain", String.class, Instrumentation.class).invoke(null, agentArgs, inst); - } - - public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { - premain(agentArgs, inst); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java b/java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java deleted file mode 100644 index a10c2cd..0000000 --- a/java_jar_agent/promagent-framework/promagent-agent/src/test/java/io/promagent/agent/JarFilesTest.java +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.agent; - -import org.junit.jupiter.api.Test; - -import static io.promagent.agent.JarFiles.findAgentJarFromCmdline; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.*; - -class JarFilesTest { - - @Test - void testCmdlineParserWildfly() { - // The command line arguments are taken from the Wildfly application server example. - String[] cmdlineArgs = new String[]{ - "-D[Standalone]", - "-Xbootclasspath/p:/tmp/wildfly-10.1.0.Final/modules/system/layers/base/org/jboss/logmanager/main/jboss-logmanager-2.0.4.Final.jar", - "-Djboss.modules.system.pkgs=org.jboss.logmanager,io.promagent.agent", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", - "-javaagent:../promagent/promagent-dist/target/promagent.jar=port=9300", - "-Dorg.jboss.boot.log.file=/tmp/wildfly-10.1.0.Final/standalone/log/server.log", - "-Dlogging.configuration=file:/tmp/wildfly-10.1.0.Final/standalone/configuration/logging.properties" - }; - assertEquals("../promagent/promagent-dist/target/promagent.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); - } - - @Test - void testCmdlineParserVersioned() { - String[] cmdlineArgs = new String[] { - "-javaagent:promagent-1.0-SNAPSHOT.jar" - }; - assertEquals("promagent-1.0-SNAPSHOT.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); - } - - @Test() - void testCmdlineParserFailed() { - String[] cmdlineArgs = new String[] { - "-javaagent:/some/other/agent.jar", - "-jar", - "promagent.jar" - }; - Exception e = assertThrows(Exception.class, () -> findAgentJarFromCmdline(asList(cmdlineArgs))); - // The exception should contain some message indicating promagent.jar was not found. - assertTrue(e.getMessage().contains("promagent.jar")); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-api/pom.xml b/java_jar_agent/promagent-framework/promagent-api/pom.xml deleted file mode 100644 index 3651380..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/pom.xml +++ /dev/null @@ -1,21 +0,0 @@ - - 4.0.0 - - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - - promagent-api - promagent-api API for implementing hooks - - jar - - - - io.prometheus - simpleclient_common - - - diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java deleted file mode 100644 index c32356a..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/After.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The method annotated with @After is executed before exiting the instrumented method. - * The method annotated with @After must have exactly the same parameters as the instrumented method. - * The "method" parameter are the names of the instrumented methods. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface After { - String[] method(); -} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java deleted file mode 100644 index 9769082..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Before.java +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The method annotated with @Before is executed before entering the instrumented method. - * The method annotated with @Before must have exactly the same parameters as the instrumented method. - * The "method" parameter are the names of the instrumented methods. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Before { - String[] method(); -} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java deleted file mode 100644 index 2c50a1b..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Hook.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The @Hook annotation indicates that the annotated class is a Hook. - * The parameter {@link Hook#instruments()} defines which classes or interfaces are instrumented by that Hook. - * The method annotations {@link Before} and {@link After} define which methods should be instrumented within that class or interface. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Hook { - - /** - * List of classes or interfaces to be instrumented. - */ - String[] instruments(); - - /** - * If true, nested calls are skipped. - * Nested means that the instrumented method calls another method that is instrumented with the same hook. - * In most cases, you would only be interested in the outer call, so the default is {@code true}. - * If set to {@code false}, nested calls will also be instrumented. - * For nested calls, the same Hook instance is re-used. - * For outer calls, a new Hook instance is created. - */ - boolean skipNestedCalls() default true; -} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java deleted file mode 100644 index 4223dd4..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Returned.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * A parameter annotated with @Returned can be used in a Hook's @After method to capture the return value of the instrumented method. - * - * Example: In order to instrument the following method: - * - *

- *     int sum(int a, int b) {...}
- * 
- * - * A Hook could use an @After method like this: - * - *
- *    {@literal @}After(method = "sum")
- *     void after(int a, int b, @Returned int sum) {...}
- * 
- * - * The parameter annotated with @Returned is optional, if the hook does not use the return value, the parameter can be omitted. - *

- * If the instrumented method terminates exceptionally, the type's default value is assigned to the parameter, - * i.e. {@code 0} for numeric types and {@code null} for reference types. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Returned {} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java deleted file mode 100644 index a37583c..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/annotations/Thrown.java +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * A parameter annotated with @Thrown can be used in a Hook's @After method to capture an Exception thrown in an instrumented method. - * - * Example: In order to instrument the following method: - * - *

- *     int div(int a, int b) {
- *         return a / b;
- *     }
- * 
- * - * A Hook could use an @After method like this: - * - *
- *    {@literal @}After(method = "div")
- *     void after(int a, int b, @Returned int result, @Thrown Throwable exception) {...}
- * 
- * - * In case everything goes well, the {@code result} will be the return value of {@code div()}, and {@code excption} will be {@code null}. - * If {@code b} is {@code 0}, the {@code result} will be {@code 0}, and {@code exception} will be an {@link ArithmeticException} (division by zero). - *

- * The parameter annotated with @Thrown is optional, if the hook does not use the exception, the parameter can be omitted. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Thrown {} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java deleted file mode 100644 index 61a118e..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricDef.java +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.hookcontext; - -import io.prometheus.client.Collector; -import io.prometheus.client.CollectorRegistry; - -import java.util.function.BiFunction; - -/** - * See {@link #MetricDef(String, BiFunction)} - */ -public class MetricDef { - - private final String metricName; - private final BiFunction producer; - - /** - * See {@link MetricsStore}. - * @param metricName Name of the Prometheus metric, like "http_requests_total". - * @param producer Function to create a new metric and register it with the registry. - * This function will only be called once, subsequent calls to {@link MetricsStore#createOrGet(MetricDef)} - * will return the previously created metric with the specified name. - * The two parameters are the metricName as specified above, - * and the Prometheus registry where the new metric should be registered. - * For an example see JavaDoc for {@link MetricsStore}. - */ - public MetricDef(String metricName, BiFunction producer) { - this.metricName = metricName; - this.producer = producer; - } - - String getMetricName() { - return metricName; - } - - BiFunction getProducer() { - return producer; - } -} diff --git a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java b/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java deleted file mode 100644 index bcd9cb2..0000000 --- a/java_jar_agent/promagent-framework/promagent-api/src/main/java/io/promagent/hookcontext/MetricsStore.java +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.hookcontext; - -import io.prometheus.client.Collector; -import io.prometheus.client.CollectorRegistry; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; - -/** - * Instead of creating Prometheus Metrics directly, Hooks should use the {@link MetricsStore} like this: - *

- * Counter httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>(
- *         "http_requests_total",
- *         (name, registry) -> Counter.build()
- *                 .name(name)
- *                 .labelNames("method", "path", "status")
- *                 .help("Total number of http requests.")
- *                 .register(registry)
- * ));
- * 
- * The Promgent framework will take care that each metric is created only once and re-used across re-deployments in an application server. - */ -public class MetricsStore { - - private final CollectorRegistry registry; - private final ConcurrentMap metrics = new ConcurrentHashMap<>(); - - public MetricsStore(CollectorRegistry registry) { - this.registry = registry; - } - - /** - * See {@link MetricsStore} and {@link MetricDef#MetricDef(String, BiFunction)}. - */ - @SuppressWarnings("unchecked") - public T createOrGet(MetricDef metricDef) { - return (T) metrics.computeIfAbsent(metricDef.getMetricName(), s -> metricDef.getProducer().apply(metricDef.getMetricName(), registry)); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-exporter/pom.xml b/java_jar_agent/promagent-framework/promagent-exporter/pom.xml deleted file mode 100644 index e3850a7..0000000 --- a/java_jar_agent/promagent-framework/promagent-exporter/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - - promagent-exporter - promagent.war - - - false - - - war - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - promagent - - diff --git a/java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java b/java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java deleted file mode 100644 index 86611d2..0000000 --- a/java_jar_agent/promagent-framework/promagent-exporter/src/main/java/io/promagent/exporter/PromagentExporterServlet.java +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.exporter; - -import javax.management.ObjectName; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.lang.management.ManagementFactory; - -/** - * This servlet simply calls the ExporterMBean via JMX and provides the result. - */ -@WebServlet("/") -public class PromagentExporterServlet extends HttpServlet { - - @Override - public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { - try { - String result = (String) ManagementFactory.getPlatformMBeanServer() - .getAttribute(new ObjectName("io.promagent:type=exporter"), "TextFormat"); - response.getWriter().println(result); - } catch (Exception e) { - response.setStatus(500); - response.getWriter().println("Failed to load Exporter MBean. Are you sure the Prometheus agent is running?"); - e.printStackTrace(); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/pom.xml b/java_jar_agent/promagent-framework/promagent-internal/pom.xml deleted file mode 100644 index 2734613..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - 4.0.0 - - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - - promagent-internal - promagent-internal classes shared across deployments - - jar - - - - - ${project.groupId} - promagent-agent - ${project.version} - provided - - - - ${project.groupId} - promagent-api - ${project.version} - provided - - - - net.bytebuddy - byte-buddy - 1.9.4 - - - - commons-io - commons-io - 2.6 - - - - io.prometheus - simpleclient_common - - - - org.junit.jupiter - junit-jupiter-api - test - - - - org.mockito - mockito-core - 2.23.0 - test - - - - javax.servlet - javax.servlet-api - 4.0.1 - test - - - - diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java deleted file mode 100644 index 8313f4e..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/BuiltInServer.java +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpServer; -import io.prometheus.client.CollectorRegistry; -import io.prometheus.client.exporter.common.TextFormat; - -import java.io.IOException; -import java.io.StringWriter; -import java.net.InetSocketAddress; -import java.util.Collections; - -/** - * Use the Java runtime's built-in {@link HttpServer} to export Prometheus metrics. - */ -class BuiltInServer { - - static void run(String host, String portString, CollectorRegistry registry) throws Exception { - try { - int port = Integer.parseInt(portString); - InetSocketAddress address = host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port); - HttpServer httpServer = HttpServer.create(address, 10); - httpServer.createContext("/", httpExchange -> { - if ("/metrics".equals(httpExchange.getRequestURI().getPath())) { - respondMetrics(registry, httpExchange); - } else { - respondRedirect(httpExchange); - } - }); - httpServer.start(); - } catch (NumberFormatException e) { - throw new RuntimeException("Failed to parse command line arguments: '" + portString + "' is not a valid port number."); - } - } - - private static void respondMetrics(CollectorRegistry registry, HttpExchange httpExchange) throws IOException { - StringWriter respBodyWriter = new StringWriter(); - respBodyWriter.write("# Metrics will become visible when they are updated for the first time.\n"); - TextFormat.write004(respBodyWriter, registry.metricFamilySamples()); - byte[] respBody = respBodyWriter.toString().getBytes("UTF-8"); - httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); - httpExchange.sendResponseHeaders(200, respBody.length); - httpExchange.getResponseBody().write(respBody); - httpExchange.getResponseBody().close(); - } - - private static void respondRedirect(HttpExchange httpExchange) throws IOException { - byte[] respBody = "Metrics are provided on the /metrics endpoint.".getBytes("UTF-8"); - httpExchange.getResponseHeaders().add("Location", "/metrics"); - httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); - httpExchange.sendResponseHeaders(302, respBody.length); - httpExchange.getResponseBody().write(respBody); - httpExchange.getResponseBody().close(); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java deleted file mode 100644 index fcc3410..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Delegator.java +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Returned; -import io.promagent.annotations.Thrown; -import io.promagent.hookcontext.MetricsStore; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Delegator is called from the Byte Buddy Advice, and calls the Hook's @Before and @After methods. - *

- * TODO: This is called often, should be performance optimized, e.g. caching hook method handles, etc. - */ -public class Delegator { - - private static Delegator instance; // not thread-safe, but it is set only once in the agent's premain method. - - private final SortedSet hookMetadata; - private final MetricsStore metricsStore; - private final ClassLoaderCache classLoaderCache; - private final ThreadLocal, Object>> threadLocal; - - private Delegator(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { - this.hookMetadata = hookMetadata; - this.metricsStore = metricsStore; - this.classLoaderCache = classLoaderCache; - this.threadLocal = ThreadLocal.withInitial(HashMap::new); - } - - public static void init(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { - instance = new Delegator(hookMetadata, metricsStore, classLoaderCache); - } - - /** - * Should be called from the Advice's @OnMethodEnter method. Returns the list of Hooks to be passed on to after() - */ - public static List before(Class interceptedClass, Method interceptedMethod, Object[] args) { - return instance.doBefore(interceptedClass, interceptedMethod, args); - } - - private List doBefore(Class interceptedClass, Method interceptedMethod, Object[] args) { - List hookInstances = loadFromThreadLocalOrCreate(interceptedClass, interceptedMethod); - for (HookInstance hookInstance : hookInstances) { - invokeBefore(hookInstance.getInstance(), interceptedMethod, args); - } - return hookInstances; - } - - /** - * Should be called from the Advice's @OnMethodExit method. First parameter is the list of hooks returned by before() - */ - public static void after(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { - instance.doAfter(hookInstances, interceptedMethod, args, returned, thrown); - } - - private void doAfter(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { - if (hookInstances != null) { - for (HookInstance hookInstance : hookInstances) { - invokeAfter(hookInstance.getInstance(), interceptedMethod, args, returned, thrown); - if (!hookInstance.isRecursiveCall()) { - threadLocal.get().remove(hookInstance.getInstance().getClass()); - } - } - } - } - - private List loadFromThreadLocalOrCreate(Class interceptedClass, Method interceptedMethod) { - return hookMetadata.stream() - .filter(hook -> classOrInterfaceMatches(interceptedClass, hook)) - .filter(hook -> methodNameAndNumArgsMatch(interceptedMethod, hook)) - .map(hook -> loadHookClass(hook)) - .filter(hookClass -> argumentTypesMatch(hookClass, interceptedMethod)) - .filter(hookClass -> ! shouldBeSkipped(hookClass)) - .map(hookClass -> loadFromTheadLocalOrCreate(hookClass)) - .collect(Collectors.toList()); - } - - private static boolean classOrInterfaceMatches(Class classToBeInstrumented, HookMetadata hook) { - Set classesAndInterfaces = getAllSuperClassesAndInterfaces(classToBeInstrumented); - return hook.getInstruments().stream().anyMatch(classesAndInterfaces::contains); - } - - private static Set getAllSuperClassesAndInterfaces(Class clazz) { - Set result = new HashSet<>(); - addAllSuperClassesAndInterfaces(clazz, result); - return result; - } - - private static void addAllSuperClassesAndInterfaces(Class clazz, Set result) { - if (clazz == null) { - return; - } - if (result.contains(clazz.getName())) { - return; - } - result.add(clazz.getName()); - for (Class ifc : clazz.getInterfaces()) { - addAllSuperClassesAndInterfaces(ifc, result); - } - addAllSuperClassesAndInterfaces(clazz.getSuperclass(), result); - } - - private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata hook) { - return hook.getMethods().stream() - .anyMatch(instrumentedMethod -> methodNameAndNumArgsMatch(interceptedMethod, instrumentedMethod)); - } - - private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata.MethodSignature instrumentedMethod) { - if (!interceptedMethod.getName().equals(instrumentedMethod.getMethodName())) { - return false; - } - if (interceptedMethod.getParameterCount() != instrumentedMethod.getParameterTypes().size()) { - return false; - } - return true; - } - - private Class loadHookClass(HookMetadata hook) { - try { - return classLoaderCache.currentClassLoader().loadClass(hook.getHookClassName()); - } catch (ClassNotFoundException e) { - throw new HookException("Failed to load Hook class " + hook.getHookClassName() + ": " + e.getMessage(), e); - } - } - - private static boolean argumentTypesMatch(Class hookClass, Method interceptedMethod) { - List before = findHookMethods(Before.class, hookClass, interceptedMethod); - List after = findHookMethods(After.class, hookClass, interceptedMethod); - return ! (before.isEmpty() && after.isEmpty()); - } - - private static List findHookMethods(Class annotation, Class hookClass, Method interceptedMethod) throws HookException { - return Stream.of(hookClass.getDeclaredMethods()) - .filter(method -> method.isAnnotationPresent(annotation)) - .filter(method -> getMethodNames(method.getAnnotation(annotation)).contains(interceptedMethod.getName())) - .filter(method -> parameterTypesMatch(method, interceptedMethod)) - .collect(Collectors.toList()); - } - - private static List getMethodNames(Annotation annotation) throws HookException { - if (Before.class.isAssignableFrom(annotation.getClass())) { - return Arrays.asList(((Before) annotation).method()); - } else if (After.class.isAssignableFrom(annotation.getClass())) { - return Arrays.asList(((After) annotation).method()); - } else { - throw new HookException("Unsupported Annotation: @" + annotation.getClass().getSimpleName() + "."); - } - } - - // TODO: We could extend this to find the "closest" match, like in Java method calls. - private static boolean parameterTypesMatch(Method hookMethod, Method interceptedMethod) { - List> hookParameterTypes = stripReturnedAndThrown(hookMethod); - if (hookParameterTypes.size() != interceptedMethod.getParameterCount()) { - return false; - } - for (int i = 0; i < hookParameterTypes.size(); i++) { - Class hookParam = hookParameterTypes.get(i); - Class interceptedParam = interceptedMethod.getParameterTypes()[i]; - if (!hookParam.equals(interceptedParam)) { - return false; - } - } - return true; - } - - private static List> stripReturnedAndThrown(Method hookMethod) { - Class[] allTypes = hookMethod.getParameterTypes(); - Annotation[][] annotations = hookMethod.getParameterAnnotations(); - if (allTypes.length != annotations.length) { - throw new HookException("Method.getParameterAnnotations() returned an unexpected value. This is a bug in promagent."); - } - List> result = new ArrayList<>(); - for (int i=0; i Returned.class.equals(a) || Thrown.class.equals(a))) { - result.add(allTypes[i]); - } - } - return result; - } - - private boolean shouldBeSkipped(Class hookClass) { - return hookClass.getAnnotation(io.promagent.annotations.Hook.class).skipNestedCalls() - && threadLocal.get().containsKey(hookClass); - } - - private HookInstance loadFromTheadLocalOrCreate(Class hookClass) { - Object existingHookInstance = threadLocal.get().get(hookClass); - if (existingHookInstance != null) { - return new HookInstance(existingHookInstance, true); - } else { - String errMsg = "Failed to create new instance of hook " + hookClass.getSimpleName() + ": "; - try { - Object newHookInstance = hookClass.getConstructor(MetricsStore.class).newInstance(metricsStore); - threadLocal.get().put(hookClass, newHookInstance); - return new HookInstance(newHookInstance, false); - } catch (NoSuchMethodException e) { - throw new HookException(errMsg + "Hook classes must have a public constructor with a single parameter of type " + MetricsStore.class.getSimpleName(), e); - } catch (Exception e) { - throw new HookException(errMsg + e.getMessage(), e); - } - } - } - - /** - * Invoke the matching Hook methods annotated with @Before - */ - private static void invokeBefore(Object hookInstance, Method interceptedMethod, Object[] args) throws HookException { - invoke(Before.class, hookInstance, interceptedMethod, args, null, null); - } - - /** - * Invoke the matching Hook methods annotated with @After - */ - private static void invokeAfter(Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { - invoke(After.class, hookInstance, interceptedMethod, args, returned, thrown); - } - - private static void invoke(Class annotation, Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { - if (args.length != interceptedMethod.getParameterCount()) { - throw new IllegalArgumentException("Number of provided arguments is " + args.length + ", but interceptedMethod expects " + interceptedMethod.getParameterCount() + " argument(s)."); - } - for (Method method : findHookMethods(annotation, hookInstance.getClass(), interceptedMethod)) { - try { - if (!method.isAccessible()) { - method.setAccessible(true); - } - method.invoke(hookInstance, addReturnedAndThrownArgs(method, args, returned, thrown)); - } catch (Exception e) { - throw new HookException("Failed to call " + method.getName() + "() on " + hookInstance.getClass().getSimpleName() + ": " + e.getMessage(), e); - } - } - } - - private static Object[] addReturnedAndThrownArgs(Method hookMethod, Object[] args, Object returned, Throwable thrown) { - Annotation[][] annotations = hookMethod.getParameterAnnotations(); - List result = new ArrayList<>(); - int arg = 0; - for (Annotation[] annotation : annotations) { - if (Arrays.stream(annotation) - .map(Annotation::annotationType) - .anyMatch(Returned.class::equals)) { - result.add(returned); - } else if (Arrays.stream(annotation) - .map(Annotation::annotationType) - .anyMatch(Thrown.class::equals)) { - result.add(thrown); - } else { - result.add(args[arg++]); - } - } - return result.toArray(); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java deleted file mode 100644 index d8fbcaa..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookException.java +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -public class HookException extends RuntimeException { - - public HookException(String message) { - super(message); - } - - public HookException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java deleted file mode 100644 index 7af2242..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookInstance.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.promagent.internal; - -public class HookInstance { - - private final Object instance; - private final boolean isRecursiveCall; // true if we have taken an existing instance from the ThreadLocal - - public HookInstance(Object instance, boolean isRecursiveCall) { - this.instance = instance; - this.isRecursiveCall = isRecursiveCall; - } - - public Object getInstance() { - return instance; - } - - public boolean isRecursiveCall() { - return isRecursiveCall; - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java deleted file mode 100644 index 9c3f877..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadata.java +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Metadata for a Hook class as parsed from the @Hook, @Before, and @After annotations. - */ -public class HookMetadata implements Comparable { - - private final String hookClassName; - private final SortedSet instruments; - private final SortedSet methods; - - public static class MethodSignature implements Comparable { - - private final String methodName; - private final List parameterTypes; - - public MethodSignature(String methodName, List parameterTypes) { - this.methodName = methodName; - this.parameterTypes = Collections.unmodifiableList(new ArrayList<>(parameterTypes)); - } - - public String getMethodName() { - return methodName; - } - - public List getParameterTypes() { - return parameterTypes; - } - - @Override - public String toString() { - return methodName + "(" + String.join(", ", parameterTypes) + ")"; - } - - @Override - public boolean equals(Object o) { - return o != null && getClass() == o.getClass() && compareTo((MethodSignature) o) == 0; - } - - @Override - public int hashCode() { - return Objects.hash(toString()); - } - - @Override - public int compareTo(MethodSignature o) { - return Comparator - .comparing(MethodSignature::getMethodName) - .thenComparing(MethodSignature::getParameterTypes, new LexicographicalComparator<>()) - .compare(this, o); - } - } - - public HookMetadata(String hookClassName, Collection instruments, Collection methods) { - this.hookClassName = hookClassName; - this.instruments = Collections.unmodifiableSortedSet(new TreeSet<>(instruments)); - this.methods = Collections.unmodifiableSortedSet(new TreeSet<>(methods)); - } - - public String getHookClassName() { - return hookClassName; - } - - public SortedSet getInstruments() { - return instruments; - } - - public SortedSet getMethods() { - return methods; - } - - @Override - public String toString() { // TODO: instruments is a Set - String delimiter = System.lineSeparator() + " * "; - return hookClassName + " instruments [" + String.join(", ", instruments) + "]:" + delimiter + String.join(delimiter, strings(methods)); - } - - @Override - public boolean equals(Object o) { - return o != null && getClass() == o.getClass() && compareTo((HookMetadata) o) == 0; - } - - @Override - public int hashCode() { - return Objects.hash(toString()); - } - - @Override - public int compareTo(HookMetadata o) { - return Comparator - .comparing(HookMetadata::getHookClassName) - .thenComparing(HookMetadata::getInstruments, new LexicographicalComparator<>()) - .thenComparing(HookMetadata::getMethods, new LexicographicalComparator<>()) - .compare(this, o); - } - - private List strings(Collection list) { - return list.stream().map(Object::toString).collect(Collectors.toList()); - } - - /** - * Compare SortedSet instances or List instances in lexicographical order. - */ - static class LexicographicalComparator, T extends Iterable> implements Comparator { - @Override - public int compare(T list1, T list2) { - Iterator iterator1 = list1.iterator(); - Iterator iterator2 = list2.iterator(); - while (iterator1.hasNext() && iterator2.hasNext()) { - int result = iterator1.next().compareTo(iterator2.next()); - if (result != 0) { - return result; - } - } - if (iterator1.hasNext()) { - return 1; - } - if (iterator2.hasNext()) { - return -1; - } - return 0; - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java deleted file mode 100644 index c69043a..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/HookMetadataParser.java +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Returned; -import io.promagent.annotations.Thrown; -import io.promagent.internal.HookMetadata.MethodSignature; -import net.bytebuddy.jar.asm.*; -import org.apache.commons.io.IOUtils; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Inspect all hook classes in a JAR file and load the metadata from the @Hook, @Before, and @After annotations. - *

- * The tricky part is that we cannot create an instance of the hook classes, because they might reference - * classes that are not available in the agent's premain phase. We need to parse the metadata without instantiating - * the hook classes. - */ -public class HookMetadataParser { - - private final SortedSet hookJars; - - public HookMetadataParser(Collection hookJars) { - this.hookJars = Collections.unmodifiableSortedSet(new TreeSet<>(hookJars)); - } - - /** - * Get the {@link HookMetadata} for the hook classes without loading the hook classes. - *

- * We unsuccessfully tried two alternative implementations before we ended up with the current one. - * The first attempt was to use reflection like this: - *

-     *     Class hookClass = HookConfig.class.getClassLoader().loadClass(hookClassName);
-     *     for (Method m : hookClass.getMethods()) {
-     *         // ... get methodConfig
-     *     }
-     * 
- * However, this throws a ClassNotFoundException in hookClass.getMethods(), because - * a hook method may have parameters like ServletRequest that are not available during agent startup. - *

- * The second attempt was to analyze the hook using Byte buddy like this: - *

-     *     TypeDescription typeDesc = typePool.describe(hookClassName).resolve();
-     *     for (MethodDescription methodDesc : typeDesc.getDeclaredMethods()) {
-     *         TypeList params = methodDesc.getParameters().asTypeList().asErasures();
-     *         // ... get methodConfig
-     *     }
-     * 
- * However, this throws a ClassNotFoundException in methodDesc.getParameters().asTypeList(). - * We did not find any way in the Byte buddy API to access a method's parameter types without loading these types. - *

- * The current implementation uses ASM directly to analyze the hook class's byte code. - */ - SortedSet parse() throws IOException, ClassNotFoundException { - return parse(className -> true); // Parse all classes found in the JAR files. - } - - /** - * See {@link #parse()}. - * - * The classNameFilter is used to parse only specific classes from the JAR files. - */ - public SortedSet parse(Predicate classNameFilter) throws IOException, ClassNotFoundException { - SortedSet result = new TreeSet<>(); - for (String className : listAllJavaClasses(hookJars, classNameFilter)) { - byte[] binaryRepresentation = readBinaryRepresentation(className); - ClassReader classReader = new ClassReader(binaryRepresentation); - HookMetadataBuilder hookMetadata = new HookMetadataBuilder(className); - classReader.accept(new ClassVisitor(Opcodes.ASM5) { - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (visible && typeEquals(desc, io.promagent.annotations.Hook.class)) { - return new AnnotationValueCollector("instruments", hookMetadata::addInstruments, Opcodes.ASM5, super.visitAnnotation(desc, visible)); - } else { - return super.visitAnnotation(desc, visible); - } - } - - @Override - public MethodVisitor visitMethod(int i, String method, String desc, String signature, String[] strings) { - List parameterTypes = Arrays.stream(Type.getArgumentTypes(desc)) - .map(Type::getClassName) - .collect(Collectors.toList()); - MethodSignatureBuilder builder = hookMetadata.newMethodSignature(parameterTypes); - return new MethodVisitor(Opcodes.ASM5, super.visitMethod(i, method, desc, signature, strings)) { - @Override - public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { - if (visible && typeEquals(desc, Returned.class, Thrown.class)) { - builder.markReturnedOrThrown(parameter); - } - return super.visitParameterAnnotation(parameter, desc, visible); - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (visible && typeEquals(desc, Before.class, After.class)) { - return new AnnotationValueCollector("method", builder::addMethodName, Opcodes.ASM5, super.visitAnnotation(desc, visible)); - } else { - return super.visitAnnotation(desc, visible); - } - } - }; - } - - }, ClassReader.SKIP_CODE); - if (hookMetadata.isComplete()) { - result.add(hookMetadata.build()); - } - } - return result; - } - - private byte[] readBinaryRepresentation(String className) throws ClassNotFoundException { - String classFileName = "/" + className.replace(".", "/") + ".class"; - try(InputStream stream = getResourceAsStream(classFileName)) { - if (stream == null) { - throw new ClassNotFoundException(className); - } - return IOUtils.toByteArray(stream); - } catch (IOException e) { - throw new ClassNotFoundException(className, e); - } - } - - private InputStream getResourceAsStream(String name) throws IOException { - for (Path hookJar : hookJars) { - try { - URL url; - // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. - if (Files.isDirectory(hookJar)) { - url = hookJar.toUri().resolve("." + name).toURL(); - } else if (Files.isRegularFile(hookJar)) { - url = new URL("jar:" + hookJar.toUri().toURL().toString() + "!" + name); - } else { - throw new IOException("Invalid JAR file or classes directory: " + hookJar); - } - return url.openStream(); - } catch (FileNotFoundException e) {} - } - throw new FileNotFoundException(name + " not found in [" + hookJars.stream().map(Path::toString).reduce("", (s1, s2) -> s1 + ", " + s2) + "]"); - } - - private boolean typeEquals(String typeDescriptor, Class... classes) { - for (Class clazz : classes) { - if (clazz.getName().equals(Type.getType(typeDescriptor).getClassName())) { - return true; - } - } - return false; - } - - - /** - * Helper to create {@link HookMetadata} isntances. - */ - private static class HookMetadataBuilder { - - private String hookClassName; - private List instruments = new ArrayList<>(); - private List methods = new ArrayList<>(); - - private HookMetadataBuilder(String hookClassName) { - this.hookClassName = hookClassName; - } - - private void addInstruments(String instruments) { - this.instruments.add(instruments); - } - - private MethodSignatureBuilder newMethodSignature(List parameterTypes) { - MethodSignatureBuilder builder = new MethodSignatureBuilder(); - for (String parameterType : parameterTypes) { - builder.addParameterType(parameterType); - } - methods.add(builder); - return builder; - } - - private HookMetadata build() { - SortedSet methodSignatures = new TreeSet<>(); - for (MethodSignatureBuilder builder : methods) { - methodSignatures.addAll(builder.build()); - } - return new HookMetadata(hookClassName, instruments, methodSignatures); - } - - private boolean isComplete() { - return hookClassName != null && instruments.size() > 0 && methods.size() > 0; - } - } - - /** - * Helper to create {@link MethodSignature} instances. - *

- * There's a slight mismatch between a {@link MethodSignatureBuilder} and a {@link MethodSignature}: - * While a {@link MethodSignature} can only contain a single method name, - * the {@link MethodSignatureBuilder} may have multiple method names. - * Therefore, the {@link #build()} method might create multiple {@link MethodSignature} instances - * out of a single {@link MethodSignatureBuilder}. - */ - private static class MethodSignatureBuilder { - - private static class ParameterType { - final String type; - boolean isReturnedOrThrown; // method parameters annotated with @Returned or @Thrown will be ignored. - - private ParameterType(String type) { - this.type = type; - this.isReturnedOrThrown = false; - } - } - - SortedSet methodNames = new TreeSet<>(); - List parameterTypes = new ArrayList<>(); - - private void addMethodName(String methodName) { - methodNames.add(methodName); - } - - private void addParameterType(String parameterType) { - parameterTypes.add(new ParameterType(parameterType)); - } - - private SortedSet build() { - List strippedParameterTypes = parameterTypes.stream() - .filter(p -> ! p.isReturnedOrThrown) - .map(p -> p.type) - .collect(Collectors.toList()); - SortedSet result = new TreeSet<>(); - for (String methodName : methodNames) { - result.add(new MethodSignature(methodName, strippedParameterTypes)); - } - return result; - } - - public void markReturnedOrThrown(int parameter) { - // We know that parameter is a valid index in parameterTypes. - parameterTypes.get(parameter).isReturnedOrThrown = true; - } - } - - /** - * Collect annotation values from @Hook(instruments = ...) or @Before(method = ...) or @After(method = ...) - *

- * Example: If the annotation is @Before(method={"service", "doFilter"}) - * then using a AnnotationValueCollector with methodName=="method" will add the call - * consumer.accept("service") and consumer.accept("doFilter") - */ - private static class AnnotationValueCollector extends AnnotationVisitor { - private final Consumer consumer; - private final String methodName; - private AnnotationValueCollector(String methodName, Consumer consumer, int api, AnnotationVisitor av) { - super(api, av); - this.consumer = consumer; - this.methodName = methodName; - } - @Override - public AnnotationVisitor visitArray(String name) { - if (methodName.equals(name)) { - return new AnnotationVisitor(Opcodes.ASM5, super.visitArray(name)) { - @Override - public void visit(String name, Object value) { - consumer.accept(value.toString()); - } - }; - } else { - return super.visitArray(name); - } - } - } - - /** - * List all Java classes found in the JAR files. - * - */ - private static Set listAllJavaClasses(Set hookJars, Predicate classNameFilter) throws IOException { - Set result = new TreeSet<>(); - for (Path hookJar : hookJars) { - // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. - if (hookJar.toFile().isDirectory()) { - try (Stream dirEntries = Files.walk(hookJar)) { - addClassNames(dirEntries.map(hookJar::relativize).map(Path::toString), result, classNameFilter); - } - } - else if (hookJar.toFile().isFile()) { - try (ZipFile zipFile = new ZipFile(hookJar.toFile())) { - addClassNames(zipFile.stream().map(ZipEntry::getName), result, classNameFilter); - } - } else { - throw new IOException(hookJar + ": Failed to read file or directory."); - } - } - return result; - } - - /** - * Convert class file paths to class names and add them to result. - */ - private static void addClassNames(Stream paths, Collection result, Predicate classNameFilter) { - paths - .filter(name -> name.endsWith(".class")) - .map(name -> name.substring(0, name.length() - ".class".length())) - .map(name -> name.startsWith("/") ? name.substring(1) : name) - .map(name -> name.replace("/", ".")) - .filter(classNameFilter) - .collect(Collectors.toCollection(() -> result)); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java deleted file mode 100644 index 4323f0d..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/Promagent.java +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import static net.bytebuddy.matcher.ElementMatchers.isAbstract; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import javax.management.ObjectName; -import java.lang.instrument.Instrumentation; -import java.lang.management.ManagementFactory; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.HookMetadata.MethodSignature; -import io.promagent.internal.jmx.Exporter; -import io.promagent.internal.jmx.PromagentCollectorRegistry; -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatchers; - -public class Promagent { - - public static void premain(String agentArgs, Instrumentation inst) { - try { - PromagentCollectorRegistry registry = new PromagentCollectorRegistry(); - ManagementFactory.getPlatformMBeanServer().registerMBean(new Exporter(registry), new ObjectName("io.promagent:type=exporter")); - Map args = parseCmdline(agentArgs); - if (args.containsKey("port")) { - BuiltInServer.run(args.get("host"), args.get("port"), registry); - } - ClassLoaderCache classLoaderCache = ClassLoaderCache.getInstance(); - List hookJars = classLoaderCache.getPerDeploymentJars(); - SortedSet hookMetadata = new HookMetadataParser(hookJars).parse(); - MetricsStore metricsStore = new MetricsStore(registry); - Delegator.init(hookMetadata, metricsStore, classLoaderCache); - printHookMetadata(hookMetadata); - - AgentBuilder agentBuilder = new AgentBuilder.Default(); - agentBuilder = applyHooks(agentBuilder, hookMetadata, classLoaderCache); - agentBuilder - .disableClassFormatChanges() - // .with(AgentBuilder.Listener.StreamWriting.toSystemError()) // use this to see exceptions thrown in instrumented code - .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) - .with(AgentBuilder.TypeStrategy.Default.REDEFINE) - .installOn(inst); - - // TODO -- the following is an experiment supporting collectors directly (in addition to hooks) - // io.prometheus.client.Collector jmxCollector = (io.prometheus.client.Collector) classLoaderCache.currentClassLoader().loadClass("io.promagent.collectors.JmxCollector").newInstance(); - // registry.registerNoJmx(jmxCollector); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - /** - * Add {@link ElementMatcher} for the hooks. - */ - private static AgentBuilder applyHooks(AgentBuilder agentBuilder, SortedSet hookMetadata, ClassLoaderCache classLoaderCache) { - Map> instruments = getInstruments(hookMetadata); - for (Map.Entry> entry : instruments.entrySet()) { - String instrumentedClassName = entry.getKey(); - Set instrumentedMethods = entry.getValue(); - agentBuilder = agentBuilder - .type(ElementMatchers.hasSuperType(named(instrumentedClassName))) - .transform(new AgentBuilder.Transformer.ForAdvice() - .include(classLoaderCache.currentClassLoader()) // must be able to load PromagentAdvice - .advice(matchAnyMethodIn(instrumentedMethods), PromagentAdvice.class.getName()) - ); - } - return agentBuilder; - } - - /** - * key: name of instrumented class or interface, value: set of instrumented methods for that class or interface - */ - public static Map> getInstruments(Set hooks) { - Map> result = new TreeMap<>(); - for (HookMetadata hookMetadata : hooks) { - for (String instruments : hookMetadata.getInstruments()) { - if (!result.containsKey(instruments)) { - result.put(instruments, new TreeSet<>()); - } - result.get(instruments).addAll(hookMetadata.getMethods()); - } - } - return result; - } - - /** - * Returns a byte buddy matcher matching any method contained in methodSignatures. - */ - public static ElementMatcher matchAnyMethodIn(Set methodSignatures) { - ElementMatcher.Junction methodMatcher = ElementMatchers.none(); - for (MethodSignature methodSignature : methodSignatures) { - ElementMatcher.Junction junction = ElementMatchers - .named(methodSignature.getMethodName()) - .and(not(isAbstract())) - .and(takesArguments(methodSignature.getParameterTypes().size())); - for (int i = 0; i < methodSignature.getParameterTypes().size(); i++) { - junction = junction.and(takesArgument(i, named(methodSignature.getParameterTypes().get(i)))); - } - methodMatcher = methodMatcher.or(junction); - } - return methodMatcher; - } - - /** - * Parse a comma-separated list of key/value pairs. Example: "host=localhost,port=9300" - */ - private static Map parseCmdline(String agentArgs) { - Map result = new HashMap<>(); - if (agentArgs != null) { - for (String keyValueString : agentArgs.split(",")) { - String[] keyValue = keyValueString.split("="); - if (keyValue.length != 2) { - throw new RuntimeException("Failed to parse command line arguments '" + agentArgs + "'. " + - "Expecting a comma-separated list of key/value pairs, as for example 'host=localhost,port=9300'."); - } - result.put(keyValue[0], keyValue[1]); - } - } - return result; - } - - private static void printHookMetadata(SortedSet hookMetadata) { - System.out.println("Promagent instrumenting the following classes or interfaces:"); - for (HookMetadata m : hookMetadata) { - System.out.println(m); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java deleted file mode 100644 index 7405db4..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/PromagentAdvice.java +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import io.promagent.agent.ClassLoaderCache; -import net.bytebuddy.implementation.bytecode.assign.Assigner; - -import java.lang.reflect.Method; -import java.util.List; - -import static net.bytebuddy.asm.Advice.*; - -public class PromagentAdvice { - - // TODO: - // Should we move this class into it's own maven module - // to make clear that it cannot reference other classes from promagent-internal? - - @OnMethodEnter - @SuppressWarnings("unchecked") - public static List before( - @This(optional = true) Object that, - @Origin Method method, - @AllArguments Object[] args - ) { - // that is null when instrumenting static methods. - Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); - try { - // The following code is equivalent to: - // return Delegator.before(that, method, args); - // However, the Delegator class will not be available in the context of the instrumented method, - // so we must use our agent class loader to load the Delegator class and do the call via reflection. - Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.promagent.internal.Delegator"); - Method beforeMethod = delegator.getMethod("before", Class.class, Method.class, Object[].class); - return (List) beforeMethod.invoke(null, clazz, method, args); - } catch (Exception e) { - System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); - e.printStackTrace(); - return null; - } - } - - @OnMethodExit(onThrowable = Throwable.class) - public static void after( - @Enter List hooks, - @This(optional = true) Object that, - @Origin Method method, - @AllArguments Object[] args, - @Return(typing = Assigner.Typing.DYNAMIC) Object returned, // support void == null and int == Integer - @Thrown Throwable thrown - ) { - try { - // The following code is equivalent to: - // Delegator.after(hooks, method, args); - // However, the Delegator class will not be available in the context of the instrumented method, - // so we must use our agent class loader to load the Delegator class and do the call via reflection. - Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.promagent.internal.Delegator"); - Method afterMethod = delegator.getMethod("after", List.class, Method.class, Object[].class, Object.class, Throwable.class); - afterMethod.invoke(null, hooks, method, args, returned, thrown); - } catch (Exception e) { - Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); - System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); - e.printStackTrace(); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java deleted file mode 100644 index 9181dfc..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Exporter.java +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.jmx; - -import io.prometheus.client.CollectorRegistry; -import io.prometheus.client.exporter.common.TextFormat; - -import java.io.IOException; -import java.io.StringWriter; - -public class Exporter implements ExporterMBean { - - private final CollectorRegistry registry; - - public Exporter(CollectorRegistry registry) { - this.registry = registry; - } - - @Override - public String getTextFormat() { - try { - StringWriter result = new StringWriter(); - TextFormat.write004(result, registry.metricFamilySamples()); - return result.toString(); - } catch (IOException e) { - throw new RuntimeException("Unexpected error when writing metrics to a String: " + e.getMessage(), e); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java deleted file mode 100644 index edba98e..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/ExporterMBean.java +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.jmx; - -public interface ExporterMBean { - String getTextFormat(); -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java deleted file mode 100644 index 2466f86..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/Metric.java +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.jmx; - -import io.prometheus.client.Collector; - -import java.util.HashMap; -import java.util.Map; - -public class Metric implements MetricMBean { - - private final Collector metric; - - Metric(Collector metric) { - this.metric = metric; - } - - /** - * @see MetricMBean#getValues() - */ - @Override - public Map, Double> getValues() { - Map, Double> result = new HashMap<>(); - for (Collector.MetricFamilySamples samples : metric.collect()) { - for (Collector.MetricFamilySamples.Sample sample : samples.samples) { - Map labels = new HashMap<>(); - for (int i = 0; i < sample.labelNames.size(); i++) { - labels.put(sample.labelNames.get(i), sample.labelValues.get(i)); - } - result.put(labels, sample.value); - } - } - return result; - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java deleted file mode 100644 index d80bc75..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/MetricMBean.java +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.jmx; - -import java.util.Map; - -public interface MetricMBean { - - /** - * Get the values in a representation that can be used in MXBeans in JMX. - *

- * The result is a map of labels -> value. - * The labels themselves are represented as a key -> value map. - */ - Map, Double> getValues(); -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java b/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java deleted file mode 100644 index 045cb72..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/main/java/io/promagent/internal/jmx/PromagentCollectorRegistry.java +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.jmx; - -import io.prometheus.client.Collector; -import io.prometheus.client.CollectorRegistry; -import io.prometheus.client.SimpleCollector; - -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import java.lang.management.ManagementFactory; -import java.lang.reflect.Field; - -/** - * This is like the regular {@link CollectorRegistry}, except that when you {@link #register(Collector)} a metric, - * the metric will also be registered as an MBean in the JMX platform server. - */ -public class PromagentCollectorRegistry extends CollectorRegistry { - - @Override - public void register(Collector metric) { - super.register(metric); - try { - ManagementFactory.getPlatformMBeanServer().registerMBean(new Metric(metric), makeObjectName((SimpleCollector) metric)); - } catch (Exception e) { - throw new RuntimeException("Failed to register Prometheus metric: " + e.getMessage(), e); - } - } - - private static ObjectName makeObjectName(SimpleCollector metric) throws MalformedObjectNameException { - return makeObjectName(getFullName(metric)); - } - - private static ObjectName makeObjectName(String fullname) throws MalformedObjectNameException { - return new ObjectName("io.promagent:type=metrics,name=" + fullname); - } - - private static String getFullName(SimpleCollector metric) { - // Unfortunately, there is no public API to get the 'fullname' of a metric. We use reflection to get it anyway. - try { - Field field = SimpleCollector.class.getDeclaredField("fullname"); - field.setAccessible(true); - return (String) field.get(metric); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException("Failed to access " + metric.getClass().getName() + ".fullname. " + - "This is probably because the internal implementation of the Prometheus client library has changed. " + - "You should adapt the Promagent accordingly.", e); - } - } - - public void registerNoJmx(Collector collector) { - super.register(collector); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java deleted file mode 100644 index 790c6a4..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataParserTest.java +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import io.promagent.annotations.*; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.SortedSet; - -class HookMetadataParserTest { - - private HookMetadataParser parser; - - @BeforeEach - void setUp() { - List classesDir = new ArrayList<>(); - classesDir.add(Paths.get(this.getClass().getProtectionDomain().getCodeSource().getLocation().getPath())); - this.parser = new HookMetadataParser(classesDir); - } - - @Hook(instruments = { - "javax.servlet.Servlet", - "javax.servlet.Filter" - }) - private static class ServletTestHook { - - @Before(method = {"service", "doFilter"}) - public void before(ServletRequest request, ServletResponse response) {} - - @After(method = {"service", "doFilter"}) - public void after(ServletRequest request, ServletResponse response) throws Exception {} - } - - @Hook(instruments = "com.example.Some") - private static class PrimitiveTypesTestHook { - - @Before(method = "arrayArgs") - void before(Object[] a, int[] b, String[] c) {} - - @Before(method = "noArgs") - void before() {} - - @Before(method = "primitiveArgs") - void before(boolean a, char b, byte c, short d, int f, float g, long h, double i) {} - - @Before(method = "boxedArgs") - void before(Boolean a, Character b, Byte c, Short d, Integer f, Float g, Long h, Double i) {} - } - - @Hook(instruments = "com.example.ReturnThrown") - private static class ReturnedAndThrownTestHook { - @After(method = "div") - void after(int a, int b, @Returned int result, @Thrown Throwable exception) {} - } - - @Test - void testServletHook() throws ClassNotFoundException, IOException { - String expected = ServletTestHook.class.getName() + " instruments [javax.servlet.Filter, javax.servlet.Servlet]:\n" + - " * doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse)\n" + - " * service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)"; - SortedSet result = parser.parse(className -> className.equals(ServletTestHook.class.getName())); - Assertions.assertEquals(1, result.size()); - Assertions.assertEquals(expected, result.first().toString()); - } - - @Test - void testPrimitiveTypes() throws ClassNotFoundException, IOException { - String expected = PrimitiveTypesTestHook.class.getName() + " instruments [com.example.Some]:\n" + - " * arrayArgs(java.lang.Object[], int[], java.lang.String[])\n" + - " * boxedArgs(java.lang.Boolean, java.lang.Character, java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Float, java.lang.Long, java.lang.Double)\n" + - " * noArgs()\n" + - " * primitiveArgs(boolean, char, byte, short, int, float, long, double)"; - SortedSet result = parser.parse(className -> className.equals(PrimitiveTypesTestHook.class.getName())); - Assertions.assertEquals(1, result.size()); - Assertions.assertEquals(expected, result.first().toString()); - } - - @Test - void testReturnedAndThrown() throws IOException, ClassNotFoundException { - String expected = ReturnedAndThrownTestHook.class.getName() + " instruments [com.example.ReturnThrown]:\n" + - " * div(int, int)"; - SortedSet result = parser.parse(className -> className.equals(ReturnedAndThrownTestHook.class.getName())); - Assertions.assertEquals(1, result.size()); - Assertions.assertEquals(expected, result.first().toString()); - } - - @Test - void testNoHook() throws ClassNotFoundException, IOException { - // Use HookMetadataParserTest as an example of a class that does not have any @Hook annotation. - SortedSet result = parser.parse(className -> className.equals(HookMetadataParserTest.class.getName())); - Assertions.assertTrue(result.isEmpty()); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java deleted file mode 100644 index 5888cc4..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/HookMetadataTest.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal; - -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import static org.junit.jupiter.api.Assertions.*; - -class HookMetadataTest { - - private HookMetadata.LexicographicalComparator> comparator = new HookMetadata.LexicographicalComparator<>(); - - @Test - void testComparatorEqualSize() { - List list1 = Arrays.asList("a", "b", "c", "e"); - List list2 = Arrays.asList("a", "b", "d", "e"); - assertEquals(-1, comparator.compare(list1, list2)); - } - - @Test - void testComparatorDifferentSize() { - List list1 = Arrays.asList("a", "b", "c"); - List list2 = Arrays.asList("a", "b"); - assertEquals(1, comparator.compare(list1, list2)); - } - - @Test - void testComparatorEqual() { - List list1 = new ArrayList<>(); - List list2 = new ArrayList<>(); - assertEquals(0, comparator.compare(list1, list2)); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java deleted file mode 100644 index 9aad2c9..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Instrumentor.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.promagent.internal.instrumentationtests; - -import io.promagent.internal.HookMetadata; -import io.promagent.internal.Promagent; -import io.promagent.internal.PromagentAdvice; -import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; - -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; - -import static io.promagent.internal.Promagent.getInstruments; - -/** - * Create an instrumented version of {@link ParameterTypesExample} for {@link ParameterTypesTest}. - * The implementation is as close as possible to the instrumentation in {@link Promagent}. - */ -public class Instrumentor { - - /** - * Returns a copy of {@link ParameterTypesExample} which will be instrumented and loaded from a temporary class loader. - */ - public static T instrument(Class classToBeInstrumented, SortedSet hookMetadata) throws Exception { - Map> instruments = getInstruments(hookMetadata); - Set instrumentedMethods = instruments.get(classToBeInstrumented.getName()); - // For examples of byte buddy tests, see net.bytebuddy.asm.AdviceTest in the byte buddy source code. - return new ByteBuddy() - .redefine(classToBeInstrumented) - .visit(Advice.to(PromagentAdvice.class).on(Promagent.matchAnyMethodIn(instrumentedMethods))) - .make() - .load(Instrumentor.class.getClassLoader(), ClassLoadingStrategy.Default.CHILD_FIRST) - .getLoaded() - .newInstance(); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java deleted file mode 100644 index 84babc9..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/LifecycleTest.java +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.instrumentationtests; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.Delegator; -import io.promagent.internal.HookMetadata; -import io.promagent.internal.instrumentationtests.classes.Fruit; -import io.promagent.internal.instrumentationtests.classes.IParameterTypesExample; -import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; -import io.promagent.internal.instrumentationtests.hooks.*; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -class LifecycleTest { - - private IParameterTypesExample parameterTypesExample; - - @BeforeEach - void setUp() throws Exception { - SortedSet hookMetadata = Util.loadHookMetadata( - LifecycleHookSkipTrue.class, - LifecycleHookSkipFalse.class - ); - ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); - parameterTypesExample = Instrumentor.instrument(ParameterTypesExample.class, hookMetadata); - MetricsStore metricsStore = Util.mockMetricsStore(); - Delegator.init(hookMetadata, metricsStore, classLoaderCache); - MethodCallCounter.reset(); - } - - /** - * Expected behavior: - *

    - *
  • For LifecycleHookSkipTrue, only the first call to parameterTypesExample.recursive() should be instrumented, - * all recursive calls should not be instrumented. - *
  • For LifecycleHookSkipFalse, all calls should be instrumented, but recursive calls are executed with the existing Hook instance, - * while non-recursive calls are executed with a new instance. - *
- */ - @Test - void testLivecycle() throws Exception { - List runConfigs = Arrays.asList( - new RecursiveRunConfig(3, 4), - new RecursiveRunConfig(2, 3), - new RecursiveRunConfig(2, 4) - ); - ExecutorService executor = Executors.newFixedThreadPool(runConfigs.size()); - for (RecursiveRunConfig runConfig : runConfigs) { - executor.submit(() -> { - for (int i=0; i cfg.nRecursiveCalls) - .max() - .getAsInt(); - - for (int nRecursiveCalls = maxNRecursiveCalls; nRecursiveCalls >= 0; nRecursiveCalls--) { - final int n = nRecursiveCalls; // copy to final variable so it can be used in lambda - int expectedNumCallsSkipFalse = runConfigs.stream() - .filter(cfg -> cfg.nRecursiveCalls >= n) // recursive calls executed: >= n - .mapToInt(cfg -> cfg.nRuns) - .sum(); - int expectedNumCallsSkipTrue = runConfigs.stream() - .filter(cfg -> cfg.nRecursiveCalls == n) // recursive calls skipped: == n - .mapToInt(cfg -> cfg.nRuns) - .sum(); - MethodCallCounter.assertNumCalls(expectedNumCallsSkipFalse, LifecycleHookSkipFalse.class, "before", nRecursiveCalls); - MethodCallCounter.assertNumCalls(expectedNumCallsSkipTrue, LifecycleHookSkipTrue.class, "before", nRecursiveCalls); - } - // Number of instances should be the same because recursive calls are executed with an existing instance. - MethodCallCounter.assertNumHookInstances(runConfigs.stream().mapToInt(cfg -> cfg.nRuns).sum(), LifecycleHookSkipFalse.class); - MethodCallCounter.assertNumHookInstances(runConfigs.stream().mapToInt(cfg -> cfg.nRuns).sum(), LifecycleHookSkipTrue.class); - } - - private static class RecursiveRunConfig { - final int nRuns; // number of runs within the same thread - final int nRecursiveCalls; // number of recursive calls within each run - - private RecursiveRunConfig(int nRuns, int nRecursiveCalls) { - this.nRuns = nRuns; - this.nRecursiveCalls = nRecursiveCalls; - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java deleted file mode 100644 index 0153274..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/MethodCallCounter.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.promagent.internal.instrumentationtests; - -import org.junit.jupiter.api.Assertions; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Track method calls, used in {@link ParameterTypesTest} - */ -public class MethodCallCounter { - - public static void reset() { - captures.clear(); - } - - public static void observe(Object hook, String methodName, Object... args) { - captures.add(new Capture(hook, methodName, args)); - } - - public static void assertNumCalls(int expectedNumberOfCalls, Class hookClass, String hookMethod, Object... expectedArgs) { - List matching = captures.stream() - .filter(c -> c.hook.getClass().equals(hookClass)) - .filter(c -> c.hookMethod.equals(hookMethod)) - .filter(c -> Arrays.equals(expectedArgs, c.args)) - .collect(Collectors.toList()); - Assertions.assertEquals(expectedNumberOfCalls, matching.size()); - } - - // special case for the varargsMixed test - public static void assertNumCalls(int expectedNumberOfCalls, Class hookClass, String hookMethod, String firstString, String... moreStrings) { - List matching = captures.stream() - .filter(c -> c.hook.getClass().equals(hookClass)) - .filter(c -> c.hookMethod.equals(hookMethod)) - .filter(c -> c.args.length == 2) - .filter(c -> Objects.equals(firstString, c.args[0])) - .filter(c -> Arrays.equals(moreStrings, (String[]) c.args[1])) - .collect(Collectors.toList()); - Assertions.assertEquals(expectedNumberOfCalls, matching.size()); - } - - public static void assertNumHookInstances(int expectedNumberOfInstances, Class hookClass) { - long actualNumberOfInstances = captures.stream() - .filter(c -> c.hook.getClass().equals(hookClass)) - .map(c -> c.hook) - .distinct() - .count(); - Assertions.assertEquals(expectedNumberOfInstances, (int) actualNumberOfInstances); - } - - private static class Capture { - final Object hook; - final String hookMethod; - final Object[] args; - - Capture(Object hook, String hookMethod, Object[] args) { - this.hook = hook; - this.hookMethod = hookMethod; - this.args = args; - } - } - - private static final List captures = Collections.synchronizedList(new ArrayList<>()); - - private MethodCallCounter() {} -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java deleted file mode 100644 index 078f140..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ParameterTypesTest.java +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.instrumentationtests; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.Delegator; -import io.promagent.internal.HookMetadata; -import io.promagent.internal.instrumentationtests.classes.Fruit; -import io.promagent.internal.instrumentationtests.classes.IParameterTypesExample; -import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; -import io.promagent.internal.instrumentationtests.hooks.OnlyAfterHook; -import io.promagent.internal.instrumentationtests.hooks.OnlyBeforeHook; -import io.promagent.internal.instrumentationtests.hooks.ParameterTypesHook; -import io.promagent.internal.instrumentationtests.hooks.TwoHooks; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -class ParameterTypesTest { - - private IParameterTypesExample parameterTypesExample; - - private final byte b = (byte) 0x23; - private final short s = (short) 42; - private final int i = 7; - private final long l = 3L; - private final float f = 0.4f; - private final double d = 0.5d; - private final boolean x = true; - private final char c = 'a'; - - private final Fruit.Orange obj1 = new Fruit.Orange(); - private final Fruit.Orange obj2 = new Fruit.Orange(); - private final Fruit.Orange obj3 = new Fruit.Orange(); - - @BeforeEach - void setUp() throws Exception { - SortedSet hookMetadata = Util.loadHookMetadata( - ParameterTypesHook.class, - TwoHooks.HookOne.class, - TwoHooks.HookTwo.class, - OnlyBeforeHook.class, - OnlyAfterHook.class - ); - ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); - parameterTypesExample = Instrumentor.instrument(ParameterTypesExample.class, hookMetadata); - MetricsStore metricsStore = Util.mockMetricsStore(); - Delegator.init(hookMetadata, metricsStore, classLoaderCache); - MethodCallCounter.reset(); - } - - @Test - void testNullArgs() { - for (int n=1; n<=2; n++) { - parameterTypesExample.objects(null, obj2, null); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", null, obj2, null); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", null, obj2, null); - } - } - - @Test - void testBeforeOrAfterMissing() { - for (int n=1; n<=2; n++) { - parameterTypesExample.objects(obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, OnlyBeforeHook.class, "before", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, OnlyAfterHook.class, "after", obj1, obj2, obj3); - } - } - - @Test - void testNoArg() { - for (int i=1; i<=2; i++) { - parameterTypesExample.noParam(); - MethodCallCounter.assertNumCalls(i, ParameterTypesHook.class, "before"); - MethodCallCounter.assertNumCalls(i, ParameterTypesHook.class, "after"); - } - } - - @Test - void testPrimitiveTypes() { - for (int n=1; n<=2; n++) { - parameterTypesExample.primitiveTypes(b, s, i, l, f, d, x, c); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", b, s, i, l, f, d, x, c); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", b, s, i, l, f, d, x, c); - } - } - - @Test - void testBoxedTypes() { - for (int n=1; n<=2; n++) { - parameterTypesExample.boxedTypes(b, s, i, l, f, d, x, c); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", b, s, i, l, f, d, x, c); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", b, s, i, l, f, d, x, c); - } - } - - @Test - void testObjects() { - for (int n=1; n<=2; n++) { - parameterTypesExample.objects(obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before2", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after2", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(0, ParameterTypesHook.class, "beforeTooLoose", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(0, ParameterTypesHook.class, "beforeTooStrict", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(2*n, ParameterTypesHook.class, "beforeAndAfter", obj1, obj2, obj3); - } - } - - @Test - void testTwoHooks() { - for (int n=1; n<=2; n++) { - parameterTypesExample.objects(obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, TwoHooks.HookOne.class, "before", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, TwoHooks.HookTwo.class, "before", obj1, obj2, obj3); - } - } - - @Test - void testPrimitiveArrays() { - byte[] byteArray = new byte[] {b}; - short[] shortArray = new short[] {s}; - int[] intArray = new int[] {i}; - long[] longArray = new long[] {l}; - float[] floatArray = new float[] {f}; - double[] doubleArray = new double[] {d}; - boolean[] booleanArray = new boolean[] {x}; - char[] charArray = new char[] {c}; - for (int n=1; n<=2; n++) { - parameterTypesExample.primitiveArrays(byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); - } - } - - @Test - void testBoxedArrays() { - Byte[] byteArray = new Byte[] {b}; - Short[] shortArray = new Short[] {s}; - Integer[] intArray = new Integer[] {i}; - Long[] longArray = new Long[] {l}; - Float[] floatArray = new Float[] {f}; - Double[] doubleArray = new Double[] {d}; - Boolean[] booleanArray = new Boolean[] {x}; - Character[] charArray = new Character[] {c}; - for (int n=1; n<=2; n++) { - parameterTypesExample.boxedArrays(byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", byteArray, shortArray, intArray, longArray, floatArray, doubleArray, booleanArray, charArray); - } - } - - @Test - void testObjectArrays() { - Object[] arr1 = new Object[] {obj1, obj2}; - Fruit[] arr2 = new Fruit[] {obj3}; - Fruit.Orange[] arr3 = new Fruit.Orange[0]; - for (int n=1; n<=2; n++) { - parameterTypesExample.objectArrays(arr1, arr2, arr3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr1, arr2, arr3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr1, arr2, arr3); - } - } - - @Test - void testGenerics() { - List list1 = Arrays.asList(obj1, obj2); - List list2 = Collections.singletonList(obj3); - List list3 = new ArrayList<>(); - for (int n=1; n<=2; n++) { - parameterTypesExample.generics(list1, list2, list3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", list1, list2, list3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", list1, list2, list3); - } - } - - @Test - void testVarargsExplicit() { - for (int n=1; n<=2; n++) { - parameterTypesExample.varargsExplicit(obj1, obj2, obj3); - parameterTypesExample.varargsExplicit(); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before"); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after"); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", obj1, obj2, obj3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", obj1, obj2, obj3); - } - } - - @Test - void testVarargsImplicit() { - Object[] arr1 = new Object[] {obj1}; - Object[] arr2 = new Object[] {}; - Object[] arr3 = null; - for (int n=1; n<=2; n++) { - parameterTypesExample.varargsImplicit(arr1); - parameterTypesExample.varargsImplicit(arr2); - parameterTypesExample.varargsImplicit(arr3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr1); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr1); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr2); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr2); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", arr3); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", arr3); - } - } - - @Test - void testVarargsMixed() { - for (int n=1; n<=2; n++) { - parameterTypesExample.varargsMixed("hello"); - parameterTypesExample.varargsMixed("hello", "world"); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", "hello"); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", "hello"); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "before", "hello", "world"); - MethodCallCounter.assertNumCalls(n, ParameterTypesHook.class, "after", "hello", "world"); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java deleted file mode 100644 index 4efd88c..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/ReturnedAndThrownTest.java +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.internal.instrumentationtests; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.Delegator; -import io.promagent.internal.HookMetadata; -import io.promagent.internal.instrumentationtests.classes.Fruit; -import io.promagent.internal.instrumentationtests.classes.IReturnedAndThrownExample; -import io.promagent.internal.instrumentationtests.classes.ReturnedAndThrownExample; -import io.promagent.internal.instrumentationtests.hooks.ReturnedAndThrownHook; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.SortedSet; - -class ReturnedAndThrownTest { - - private IReturnedAndThrownExample returnedAndThrownExample; - private final Fruit.Orange orange = new Fruit.Orange(); - - @BeforeEach - void setUp() throws Exception { - SortedSet hookMetadata = Util.loadHookMetadata( - ReturnedAndThrownHook.class - ); - ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); - returnedAndThrownExample = Instrumentor.instrument(ReturnedAndThrownExample.class, hookMetadata); - MetricsStore metricsStore = Util.mockMetricsStore(); - Delegator.init(hookMetadata, metricsStore, classLoaderCache); - MethodCallCounter.reset(); - } - - @Test - void testVoid() { - for (int n=1; n<=2; n++) { - returnedAndThrownExample.returnVoid(orange); - MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "beforeVoid", orange); - MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "afterVoid", orange); - } - } - - @Test - void testPrimitive() { - for (int n=1; n<=2; n++) { - int ret = returnedAndThrownExample.returnPrimitive(orange); - MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "beforePrimitive", orange); - MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "afterPrimitive", orange, ret); - } - } - - @Test - void testObject() { - Fruit ret1 = returnedAndThrownExample.returnObject(); - Fruit ret2 = returnedAndThrownExample.returnObject(); - MethodCallCounter.assertNumCalls(2, ReturnedAndThrownHook.class, "beforeObject"); - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterObject", ret1); - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterObject", ret2); - } - - @Test - void testArray() { - int[] params = {1, 2, 3}; - int[] ret1 = returnedAndThrownExample.returnArray(params); - int[] ret2 = returnedAndThrownExample.returnArray(params); - MethodCallCounter.assertNumCalls(2, ReturnedAndThrownHook.class, "beforeArray", new Object[]{params}); - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterArray", ret1, params); - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterArray", ret2, params); - } - - @Test - void testGenerics() { - for (int n=1; n<=2; n++) { - List ret = returnedAndThrownExample.returnGenerics(orange); - MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "beforeGenerics", orange); - // The following works because List.equals() compares the elements within the lists, not the lists itself. - // Therefore both return values are considered equal, because they are both lists containing the same element. - MethodCallCounter.assertNumCalls(n, ReturnedAndThrownHook.class, "afterGenerics", orange, ret); - } - } - - @Test - void testRuntimeException() { - try { - returnedAndThrownExample.throwsRuntimeException(7, orange); - } catch (Exception e) { - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "beforeThrowsRuntimeException", 7, orange); - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterThrowsRuntimeException", 7, orange, null, e); - return; // success - } - Assertions.fail("exception not thrown."); - } - - @Test - void testCheckedException() { - try { - returnedAndThrownExample.throwsCheckedException(); - } catch (Exception e) { - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "beforeThrowsCheckedException"); - MethodCallCounter.assertNumCalls(1, ReturnedAndThrownHook.class, "afterThrowsCheckedException", 0, e); - return; // success - } - Assertions.fail("exception not thrown."); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java deleted file mode 100644 index c27d58d..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/StaticFinalTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.promagent.internal.instrumentationtests; - -import static io.promagent.internal.Promagent.getInstruments; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.Type; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.Delegator; -import io.promagent.internal.HookMetadata; -import io.promagent.internal.Promagent; -import io.promagent.internal.PromagentAdvice; -import io.promagent.internal.instrumentationtests.classes.StaticFinalExample; -import io.promagent.internal.instrumentationtests.hooks.StaticFinalTestHook; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -class StaticFinalTest { - - private Object example; - - @BeforeEach - void setUp() throws Exception { - SortedSet hookMetadata = Util.loadHookMetadata(StaticFinalTestHook.class); - ClassLoaderCache classLoaderCache = Util.mockClassLoaderCache(); - - MetricsStore metricsStore = Util.mockMetricsStore(); - Delegator.init(hookMetadata, metricsStore, classLoaderCache); - MethodCallCounter.reset(); - - Map> instruments = getInstruments(hookMetadata); - Set instrumentedMethods = instruments.get(StaticFinalExample.class.getName()); - example = new ByteBuddy() - .redefine(StaticFinalExample.class) - .visit(Advice.to(PromagentAdvice.class).on(Promagent.matchAnyMethodIn(instrumentedMethods))) - .make() - .load(this.getClass().getClassLoader(), ClassLoadingStrategy.Default.CHILD_FIRST) - .getLoaded() - .newInstance(); - } - - @Test - void testPublicStaticMethod() throws Exception { - int expectedTotalHookCalls = 0; - for (String methodName : new String[] { - "helloPublic", - "helloPublicFinal", - "helloPublicStatic", - "helloPublicStaticFinal" - }) { - Method method = example.getClass().getMethod(methodName, String.class); - method.invoke(example, "world"); - expectedTotalHookCalls++; - MethodCallCounter.assertNumCalls(expectedTotalHookCalls, StaticFinalTestHook.class, "before", new Object[]{"world"}); - MethodCallCounter.assertNumCalls(expectedTotalHookCalls, StaticFinalTestHook.class, "after", new Object[]{"world"}); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java deleted file mode 100644 index e23cbe7..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/Util.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.promagent.internal.instrumentationtests; - -import io.promagent.agent.ClassLoaderCache; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.HookMetadata; -import io.promagent.internal.HookMetadataParser; -import org.mockito.Mockito; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.SortedSet; -import java.util.stream.Stream; - -class Util { - - static SortedSet loadHookMetadata(Class... hooks) throws IOException, ClassNotFoundException { - List classesDir = new ArrayList<>(); - classesDir.add(Paths.get(Util.class.getProtectionDomain().getCodeSource().getLocation().getPath())); - HookMetadataParser parser = new HookMetadataParser(classesDir); - return parser.parse(className -> Stream.of(hooks).anyMatch(hookClass -> hookClass.getName().equals(className))); - } - - static ClassLoaderCache mockClassLoaderCache() throws NoSuchFieldException, IllegalAccessException { - ClassLoaderCache mockedClassLoaderCache = Mockito.mock(ClassLoaderCache.class); - Mockito.when(mockedClassLoaderCache.currentClassLoader()).thenReturn(Thread.currentThread().getContextClassLoader()); - Field instance = ClassLoaderCache.class.getDeclaredField("instance"); - instance.setAccessible(true); - instance.set(null, mockedClassLoaderCache); - return mockedClassLoaderCache; - } - - static MetricsStore mockMetricsStore() { - return Mockito.mock(MetricsStore.class); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java deleted file mode 100644 index 4d289a5..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/Fruit.java +++ /dev/null @@ -1,8 +0,0 @@ -package io.promagent.internal.instrumentationtests.classes; - -/** - * Example of object oriented inheritance. - */ -public class Fruit { - public static class Orange extends Fruit{} -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java deleted file mode 100644 index 324f72c..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IParameterTypesExample.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.promagent.internal.instrumentationtests.classes; - -import io.promagent.internal.instrumentationtests.Instrumentor; - -import java.util.List; - -/** - * Example methods to be instrumented with hooks. - * We use an interface to call these methods, because the actual implementation will come from a temporary - * class loader defined in {@link Instrumentor}, which cannot be used directly. - */ -public interface IParameterTypesExample { - - // TODO: add tests for enums and lambdas - - void noParam(); - - void primitiveTypes(byte b, short s, int i, long l, float f, double d, boolean x, char c); - - void boxedTypes(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c); - - void objects(Object o, Fruit f, Fruit.Orange x); - - void primitiveArrays(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c); - - void boxedArrays(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c); - - void objectArrays(Object[] o, Fruit[] f, Fruit.Orange[] x); - - void generics(List objectList, List fruitList, List orangeList); - - void varargsExplicit(Object... args); - - void varargsImplicit(Object[] args); - - void varargsMixed(String s, String... more); - - void recursive(int nRecursiveCalls); -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java deleted file mode 100644 index 5e513fd..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/IReturnedAndThrownExample.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.promagent.internal.instrumentationtests.classes; - -import java.io.IOException; -import java.util.List; - -public interface IReturnedAndThrownExample { - - // TODO: Some more return types that should be tested: Enums, Lambdas - - void returnVoid(Fruit f); - - int returnPrimitive(Fruit.Orange orange); - - Fruit returnObject(); - - int[] returnArray(int... params); - - List returnGenerics(T fruit); - - String throwsRuntimeException(int a, Fruit.Orange b); - - int throwsCheckedException() throws IOException; -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java deleted file mode 100644 index 1f29564..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ParameterTypesExample.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.promagent.internal.instrumentationtests.classes; - -import io.promagent.internal.instrumentationtests.Instrumentor; - -import java.util.List; - -/** - * These methods will be instrumented by {@link Instrumentor}. - */ -public class ParameterTypesExample implements IParameterTypesExample { - - @Override - public void noParam() {} - - @Override - public void primitiveTypes(byte b, short s, int i, long l, float f, double d, boolean x, char c) {} - - @Override - public void boxedTypes(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c) {} - - @Override - public void objects(Object o, Fruit f, Fruit.Orange x) {} - - @Override - public void primitiveArrays(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c) {} - - @Override - public void boxedArrays(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c) {} - - @Override - public void objectArrays(Object[] o, Fruit[] f, Fruit.Orange[] x) {} - - @Override - public void generics(List objectList, List fruitList, List orangeList) {} - - @Override - public void varargsExplicit(Object... args) {} - - @Override - public void varargsImplicit(Object[] args) {} - - @Override - public void varargsMixed(String s, String... more) {} - - @Override - public void recursive(int nRecursiveCalls) { - if (nRecursiveCalls > 0) { - recursive(nRecursiveCalls - 1); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java deleted file mode 100644 index a6a66d5..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/ReturnedAndThrownExample.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.promagent.internal.instrumentationtests.classes; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; - -public class ReturnedAndThrownExample implements IReturnedAndThrownExample { - - @Override - public void returnVoid(Fruit f) {} - - @Override - public int returnPrimitive(Fruit.Orange orange) { - return 42; - } - - @Override - public Fruit returnObject() { - return new Fruit.Orange(); - } - - @Override - public int[] returnArray(int... params) { - return new int[] {23, 42}; - } - - @Override - public List returnGenerics(T fruit) { - return Collections.singletonList(fruit); - } - - @Override - public String throwsRuntimeException(int a, Fruit.Orange b) { - Object n = null; - return n.toString(); // throws NullPointerException - } - - @Override - public int throwsCheckedException() throws IOException { - throw new IOException(); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java deleted file mode 100644 index 57dce6b..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/classes/StaticFinalExample.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.promagent.internal.instrumentationtests.classes; - -public class StaticFinalExample { - - public String helloPublic(String name) { - return "hello public " + name; - } - - public final String helloPublicFinal(String name) { - return "hello public final " + name; - } - - public static String helloPublicStatic(String name) { - return "hello public static " + name; - } - - public static String helloPublicStaticFinal(String name) { - return "hello public static final " + name; - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java deleted file mode 100644 index d29fec0..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipFalse.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; -import io.promagent.internal.instrumentationtests.classes.Fruit.Orange; -import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; - -/** - * Instrument all methods in {@link ParameterTypesExample}. - */ -@Hook( - instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample", - skipNestedCalls = false -) -public class LifecycleHookSkipFalse { - - public LifecycleHookSkipFalse(MetricsStore m) {} - - @Before(method = "recursive") - public void before(int n) { - MethodCallCounter.observe(this, "before", n); - } - - @After(method = "recursive") - public void after(int n) { - MethodCallCounter.observe(this, "after", n); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java deleted file mode 100644 index 73f808b..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/LifecycleHookSkipTrue.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; -import io.promagent.internal.instrumentationtests.classes.Fruit.Orange; -import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; - -import java.util.List; - -/** - * Instrument all methods in {@link ParameterTypesExample}. - */ -@Hook( - instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample", - skipNestedCalls = true -) -public class LifecycleHookSkipTrue { - - public LifecycleHookSkipTrue(MetricsStore m) {} - - @Before(method = "recursive") - public void before(int n) { - MethodCallCounter.observe(this, "before", n); - } - - @After(method = "recursive") - public void after(int n) { - MethodCallCounter.observe(this, "after", n); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java deleted file mode 100644 index 76d3b10..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyAfterHook.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; - -/** - * Test hook with no @Before method. - */ -@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") -public class OnlyAfterHook { - - public OnlyAfterHook(MetricsStore m) {} - - @After(method = "objects") - public void after(Object o, Fruit f, Fruit.Orange x) { - MethodCallCounter.observe(this, "after", o, f, x); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java deleted file mode 100644 index d671514..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/OnlyBeforeHook.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; - -/** - * Test hook with no @After method - */ -@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") -public class OnlyBeforeHook { - - public OnlyBeforeHook(MetricsStore m) {} - - @Before(method = "objects") - public void before(Object o, Fruit f, Fruit.Orange x) { - MethodCallCounter.observe(this, "before", o, f, x); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java deleted file mode 100644 index c9a724d..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ParameterTypesHook.java +++ /dev/null @@ -1,167 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; -import io.promagent.internal.instrumentationtests.classes.Fruit.Orange; -import io.promagent.internal.instrumentationtests.classes.ParameterTypesExample; - -import java.util.List; - -/** - * Instrument all methods in {@link ParameterTypesExample}. - */ -@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") -public class ParameterTypesHook { - - public ParameterTypesHook(MetricsStore m) {} - - @Before(method = "noParam") - public void before() { - MethodCallCounter.observe(this, "before"); - } - - @After(method = "noParam") - public void after() { - MethodCallCounter.observe(this, "after"); - } - - @Before(method = {"primitiveTypes", "boxedTypes"}) // "boxedTypes" should be ignored because different method signature - public void before(byte b, short s, int i, long l, float f, double d, boolean x, char c) { - MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); - } - - @After(method = {"primitiveTypes", "boxedTypes"}) // "boxedTypes" should be ignored because different method signature - public void after(byte b, short s, int i, long l, float f, double d, boolean x, char c) { - MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); - } - - @Before(method = {"primitiveTypes", "boxedTypes"}) // "primitiveTypes" should be ignored because different method signature - public void before(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c) { - MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); - } - - @After(method = {"primitiveTypes", "boxedTypes"}) // "primitiveTypes" should be ignored because different method signature - public void after(Byte b, Short s, Integer i, Long l, Float f, Double d, Boolean x, Character c) { - MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); - } - - @Before(method = "objects") - public void before(Object o, Fruit f, Orange x) { - MethodCallCounter.observe(this, "before", o, f, x); - } - - @After(method = "objects") - public void after(Object o, Fruit f, Orange x) { - MethodCallCounter.observe(this, "after", o, f, x); - } - - @Before(method = "objects") - public void before2(Object o, Fruit f, Orange x) { - MethodCallCounter.observe(this, "before2", o, f, x); - } - - @After(method = "objects") - public void after2(Object o, Fruit f, Orange x) { - MethodCallCounter.observe(this, "after2", o, f, x); - } - - @Before(method = "objects") // should not be called, because method signature differs - public void beforeTooLoose(Object o, Fruit f, Fruit x) { - MethodCallCounter.observe(this, "beforeTooLoose", o, f, x); - } - - @Before(method = "objects") // should not be called, because method signature differs - public void beforeTooStrict(Object o, Orange f, Orange x) { - MethodCallCounter.observe(this, "beforeTooStrict", o, f, x); - } - - @Before(method = "objects") - @After(method = "objects") - public void beforeAndAfter(Object o, Fruit f, Orange x) { - MethodCallCounter.observe(this, "beforeAndAfter", o, f, x); - } - - @Before(method = "primitiveArrays") - public void before(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c) { - MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); - } - - @After(method = "primitiveArrays") - public void after(byte[] b, short[] s, int[] i, long[] l, float[] f, double[] d, boolean[] x, char[] c) { - MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); - } - - @Before(method = "boxedArrays") - public void before(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c) { - MethodCallCounter.observe(this, "before", b, s, i, l, f, d, x, c); - } - - @After(method = "boxedArrays") - public void after(Byte[] b, Short[] s, Integer[] i, Long[] l, Float[] f, Double[] d, Boolean[] x, Character[] c) { - MethodCallCounter.observe(this, "after", b, s, i, l, f, d, x, c); - } - - @Before(method = "objectArrays") - public void before(Object[] o, Fruit[] f, Orange[] x) { - MethodCallCounter.observe(this, "before", o, f, x); - } - - @After(method = "objectArrays") - public void after(Object[] o, Fruit[] f, Orange[] x) { - MethodCallCounter.observe(this, "after", o, f, x); - } - - @Before(method = "generics") - public void before(List o, List f, List x) { - MethodCallCounter.observe(this, "before", o, f, x); - } - - @After(method = "generics") - public void after(List o, List f, List x) { - MethodCallCounter.observe(this, "after", o, f, x); - } - - @Before(method = "varargsExplicit") - public void before(Object... args) { - MethodCallCounter.observe(this, "before", args); - } - - @After(method = "varargsExplicit") - public void after(Object... args) { - MethodCallCounter.observe(this, "after", args); - } - - @Before(method = "varargsImplicit") - public void before2(Object[] args) { - MethodCallCounter.observe(this, "before", args); - } - - @After(method = "varargsImplicit") - public void after2(Object[] args) { - MethodCallCounter.observe(this, "after", args); - } - - @Before(method = "varargsMixed") - public void before(String s, String... more) { - MethodCallCounter.observe(this, "before", s, more); - } - - @After(method = "varargsMixed") - public void after(String s, String... more) { - MethodCallCounter.observe(this, "after", s, more); - } - - @Before(method = "recursive") - public void before(int n) { - MethodCallCounter.observe(this, "before", n); - } - - @After(method = "recursive") - public void after(int n) { - MethodCallCounter.observe(this, "after", n); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java deleted file mode 100644 index 97435c7..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/ReturnedAndThrownHook.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.*; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; - -import java.util.List; - -@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ReturnedAndThrownExample") -public class ReturnedAndThrownHook { - - public ReturnedAndThrownHook(MetricsStore m) {} - - @Before(method = "returnVoid") - public void beforeVoid(Fruit f) { - MethodCallCounter.observe(this, "beforeVoid", f); - } - - @Before(method = "returnVoid") - public void afterVoid(Fruit f) { - MethodCallCounter.observe(this, "afterVoid", f); - } - - @Before(method = "returnPrimitive") - public void beforePrimitive(Fruit.Orange o) { - MethodCallCounter.observe(this, "beforePrimitive", o); - } - - @After(method = "returnPrimitive") - public void afterPrimitive(Fruit.Orange o, @Returned int i) { - MethodCallCounter.observe(this, "afterPrimitive", o, i); - } - - @Before(method = "returnObject") - public void beforeObject() { - MethodCallCounter.observe(this, "beforeObject"); - } - - @After(method = "returnObject") - public void afterObject(@Returned Fruit f) { - MethodCallCounter.observe(this, "afterObject", f); - } - - @Before(method = "returnArray") - public void beforeArray(int... params) { - MethodCallCounter.observe(this, "beforeArray", new Object[]{params}); - } - - @After(method = "returnArray") - public void afterArray(@Returned int[] ret, int... params) { - MethodCallCounter.observe(this, "afterArray", ret, params); - } - - @Before(method = "returnGenerics") - public void beforeGenerics(T fruit) { - MethodCallCounter.observe(this, "beforeGenerics", fruit); - } - - @After(method = "returnGenerics") - public void afterGenerics(T fruit, @Returned List ret) { - MethodCallCounter.observe(this, "afterGenerics", fruit, ret); - } - - @Before(method = "throwsRuntimeException") - public void beforeThrowsRuntimeException(int a, Fruit.Orange o) { - MethodCallCounter.observe(this, "beforeThrowsRuntimeException", a, o); - } - - @After(method = "throwsRuntimeException") - public void afterThrowsRuntimeException(int a, Fruit.Orange o, @Returned String ret, @Thrown Throwable e) { - MethodCallCounter.observe(this, "afterThrowsRuntimeException", a, o, ret, e); - } - - @Before(method = "throwsCheckedException") - public void beforeThrowsCheckedException() { - MethodCallCounter.observe(this, "beforeThrowsCheckedException"); - } - - @After(method = "throwsCheckedException") - public void afterThrowsCheckedException(@Returned int ret, @Thrown Throwable e) { - MethodCallCounter.observe(this, "afterThrowsCheckedException", ret, e); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java deleted file mode 100644 index 0dcc76a..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/StaticFinalTestHook.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; - -@Hook(instruments = "io.promagent.internal.instrumentationtests.classes.StaticFinalExample") -public class StaticFinalTestHook { - - public StaticFinalTestHook(MetricsStore m) {} - - @Before(method = { - "helloPublic", - "helloPublicFinal", - "helloPublicStatic", - "helloPublicStaticFinal" - }) - public void before(String name) { - MethodCallCounter.observe(this, "before", name); - } - - @After(method = { - "helloPublic", - "helloPublicFinal", - "helloPublicStatic", - "helloPublicStaticFinal" - }) - public void after(String name) { - MethodCallCounter.observe(this, "after", name); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java b/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java deleted file mode 100644 index 478672d..0000000 --- a/java_jar_agent/promagent-framework/promagent-internal/src/test/java/io/promagent/internal/instrumentationtests/hooks/TwoHooks.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.promagent.internal.instrumentationtests.hooks; - -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricsStore; -import io.promagent.internal.instrumentationtests.MethodCallCounter; -import io.promagent.internal.instrumentationtests.classes.Fruit; - -/** - * Two hooks instrumenting the same class. - */ -public class TwoHooks { - - @Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") - public static class HookOne { - - public HookOne(MetricsStore m) {} - - @Before(method = "objects") - public void before(Object o, Fruit f, Fruit.Orange x) { - MethodCallCounter.observe(this, "before", o, f, x); - } - } - - @Hook(instruments = "io.promagent.internal.instrumentationtests.classes.ParameterTypesExample") - public static class HookTwo { - - public HookTwo(MetricsStore m) {} - - @Before(method = "objects") - public void before(Object o, Fruit f, Fruit.Orange x) { - MethodCallCounter.observe(this, "before", o, f, x); - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-loader/README.md b/java_jar_agent/promagent-framework/promagent-loader/README.md deleted file mode 100644 index 802f4f8..0000000 --- a/java_jar_agent/promagent-framework/promagent-loader/README.md +++ /dev/null @@ -1,20 +0,0 @@ -promagent-loader ----------------- - -Experimental tool for loading the promagent into an existing JVM. Usage: - -```bash -java -cp $JAVA_HOME/lib/tools.jar:/path/to/promagent-loader.jar io.promagent.loader.PromagentLoader -agent /path/to/promagent.jar -port 9300 -pid -``` - -The `promagent-loader` uses OpenJDK API and will probably not work with other Java VMs. - -With JDK 8 or earlier, the file `$JAVA_HOME/lib/tools.jar` must exist. With JDK 9 or higher, the loader runs without this external dependency, because the classes have been moved into the Java runtime. - -The `/path/to/promagent.jar` must be an absolute path, not a relative path. - -The port is the TCP port for the exporter, like when promagent is attached on JVM startup with parameter `-javaagent:/path/to/promagent.jar=port=9300`. - -The `` is the PID of the Java process that the agent should attach to. It can be found with the `jps` command. - -See [https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent](https://github.com/raphw/byte-buddy/tree/master/byte-buddy-agent) for a more portable implementation of an agent loader. diff --git a/java_jar_agent/promagent-framework/promagent-loader/pom.xml b/java_jar_agent/promagent-framework/promagent-loader/pom.xml deleted file mode 100644 index 1afa096..0000000 --- a/java_jar_agent/promagent-framework/promagent-loader/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - 4.0.0 - - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - - promagent-loader - promagent-loader attach promagent to a running JVM - - jar - - - - org.junit.jupiter - junit-jupiter-api - test - - - - com.sun - tools - 1.8 - system - ${java.home}/../lib/tools.jar - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - io.promagent.loader.PromagentLoader - - - - - - - diff --git a/java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java b/java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java deleted file mode 100644 index 4843ecf..0000000 --- a/java_jar_agent/promagent-framework/promagent-loader/src/main/java/io/promagent/loader/PromagentLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.promagent.loader; - -import com.sun.tools.attach.AgentLoadException; -import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; - -public class PromagentLoader { - - public static void main(String[] args) throws Exception { - int pid = getIntArg(args, "-pid"); - int port = getIntArg(args, "-port"); - String agentJar = getStringArg(args, "-agent"); - PromagentLoader.loadPromagent(agentJar, pid, port); - } - - private static void loadPromagent(String agentJar, int pid, int port) throws Exception { - VirtualMachineDescriptor vmd = findVirtualMachine(Integer.toString(pid)); - if (vmd == null) { - System.err.println("No Java process found with PID " + pid); - System.exit(-1); - } - VirtualMachine vm = null; - try { - vm = VirtualMachine.attach(vmd); - vm.loadAgent(agentJar, "port=" + port); - } catch (AgentLoadException e) { - System.err.println("Failed to attach agent: " + getMessage(e)); - } finally { - if (vm != null) { - vm.detach(); - } - } - } - - private static VirtualMachineDescriptor findVirtualMachine(String pid) { - for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { - if (vmd.id().equalsIgnoreCase(pid)) { - return vmd; - } - } - return null; - } - - private static String getMessage(AgentLoadException e) { - switch (e.getMessage()) { - case "-4": - return "Insuffient memory"; - case "100": - return "Agent JAR not found or no Agent-Class attribute"; - case "101": - return "Unable to add JAR file to system class path"; - case "102": - return "Agent JAR loaded but agent failed to initialize"; - default: - return e.getMessage(); - } - } - - private static int getIntArg(String[] args, String option) { - String stringArg = getStringArg(args, option); - try { - return Integer.parseInt(stringArg); - } catch (NumberFormatException e) { - System.err.println(option + " " + stringArg + ": invalid argument"); - System.exit(-1); - return 0; // will never happen - } - } - - private static String getStringArg(String[] args, String option) { - for (int pos : new int[]{0, 2, 4}) { - if (args.length < pos + 2) { - printUsageAndExit(); - } - if (option.equals(args[pos])) { - return args[pos+1]; - } - } - printUsageAndExit(); - return null; // will never happen - } - - private static void printUsageAndExit() { - System.err.println("Usage: java -cp $JAVA_HOME/lib/tools.jar:/path/to/promagent-loader.jar io.promagent.loader.PromagentLoader -agent /path/to/promagent.jar -port 9300 -pid "); - System.exit(-1); - } -} diff --git a/java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml b/java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml deleted file mode 100644 index b428f60..0000000 --- a/java_jar_agent/promagent-framework/promagent-maven-plugin/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ - - 4.0.0 - - - io.promagent - promagent-framework - 1.0-SNAPSHOT - - - promagent-maven-plugin - promagent-maven-plugin - - maven-plugin - - - - - io.promagent - promagent-internal - 1.0-SNAPSHOT - runtime - - - - io.promagent - promagent-agent - 1.0-SNAPSHOT - runtime - - - - io.promagent - promagent-api - 1.0-SNAPSHOT - runtime - - - - org.apache.maven - maven-plugin-api - 3.6.0 - provided - - - - org.apache.maven - maven-core - 3.6.0 - provided - - - - org.apache.maven.plugin-tools - maven-plugin-annotations - 3.6.0 - provided - - - - - - - - org.apache.maven.plugins - maven-plugin-plugin - 3.5 - - promagent - - - - - diff --git a/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java b/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java deleted file mode 100644 index 42497d5..0000000 --- a/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentDependencies.java +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.plugin; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.descriptor.PluginDescriptor; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; - -import static java.util.stream.Collectors.toList; - -/** - * Hard-coded list of JAR files that must go into the agent.jar. - * Versions are resolved through Maven. - * If the user adds a duplicate JAR with a different version, the plugin fails. - */ -class AgentDependencies { - - private final String pluginGroupId; - private final List dependencies; - - /** - * Hard-coded dependencies without versions (versions are resolved dynamically). - */ - private static class ExpectedDependency { - - final String groupId; - final String artifactId; - - private ExpectedDependency(String groupId, String artifactId) { - this.groupId = groupId; - this.artifactId = artifactId; - } - } - - private AgentDependencies(String pluginGroupId, List dependencies) { - this.pluginGroupId = pluginGroupId; - this.dependencies = dependencies; - } - - static AgentDependencies init(PluginDescriptor pluginDescriptor) throws MojoExecutionException { - - String pluginGroupId = pluginDescriptor.getGroupId(); - String pluginArtifactId = pluginDescriptor.getArtifactId(); - - List expectedDependencies = Arrays.asList( - new ExpectedDependency(pluginGroupId, "promagent-agent"), - new ExpectedDependency(pluginGroupId, "promagent-internal"), - new ExpectedDependency(pluginGroupId, "promagent-api"), - new ExpectedDependency("io.prometheus", "simpleclient_common"), - new ExpectedDependency("io.prometheus", "simpleclient"), - new ExpectedDependency("net.bytebuddy", "byte-buddy"), - new ExpectedDependency("commons-io", "commons-io") - ); - - List actualDependencies = resolveVersions(pluginDescriptor, pluginArtifactId, expectedDependencies); - failUnlessComplete(actualDependencies, expectedDependencies, pluginArtifactId); - return new AgentDependencies(pluginGroupId, actualDependencies); - } - - /** - * Artifact for the promagent-agent module. - */ - Artifact getAgentArtifact() { - return dependencies.stream() - .filter(isAgent()) - .findFirst() - .get(); // We know it's present. - } - - /** - * Artifact for all other runtime dependencies except promagent-agent. - */ - List getDependencies() { - return dependencies.stream() - .filter(isAgent().negate()) - .collect(toList()); - } - - void assertNoConflict(Artifact artifact) throws MojoExecutionException { - Optional builtInVersion = dependencies.stream() - .filter(dependency -> dependency.getGroupId().equals(artifact.getGroupId())) - .filter(dependency -> dependency.getArtifactId().equals(artifact.getArtifactId())) - .map(Artifact::getVersion) - .findFirst(); - if (builtInVersion.isPresent() && ! builtInVersion.get().equals(artifact.getVersion())) { - String artifactName = artifact.getGroupId() + ":" + artifact.getArtifactId(); - throw new MojoExecutionException("Conflicting dependencies: Your project includes " + artifactName + - " version " + artifact.getVersion() + " but the promagent-maven-plugin is built with version " + builtInVersion.get()); - } - } - - private static List resolveVersions(PluginDescriptor pluginDescriptor, String pluginArtifactId, List expectedDependencies) throws MojoExecutionException { - List actualDependencies = new ArrayList<>(); - for (Artifact artifact : pluginDescriptor.getArtifacts()) { - if (! isExpected(artifact, expectedDependencies)) { - continue; - } - if (isKnown(artifact, actualDependencies)) { - continue; - } - failOnVersionConflict(artifact, actualDependencies, pluginArtifactId); - actualDependencies.add(artifact); - } - return actualDependencies; - } - - private Predicate isAgent() { - return artifact -> artifact.getGroupId().equals(pluginGroupId) && artifact.getArtifactId().equals("promagent-agent"); - } - - - private static Predicate expectedDependencyMatcher(Artifact artifact) { - return expectedDependency -> expectedDependency.groupId.equals(artifact.getGroupId()) && - expectedDependency.artifactId.equals(artifact.getArtifactId()); - } - - private static Predicate expectedDependencyMatcher(ExpectedDependency expectedDependency) { - return artifact -> expectedDependencyMatcher(artifact).test(expectedDependency); - } - - private static Predicate artifactMatcherWithoutVersion(Artifact artifact) { - return other -> artifact.getGroupId().equals(other.getGroupId()) && - artifact.getArtifactId().equals(other.getArtifactId()); - } - - private static Predicate artifactMatcherWithVersion(Artifact artifact) { - return other -> artifactMatcherWithoutVersion(artifact).test(other) && - artifact.getVersion().equals(other.getVersion()); - } - - private static boolean isExpected(Artifact artifact, List expectedDependencies) { - return expectedDependencies.stream().anyMatch(expectedDependencyMatcher(artifact)); - } - - private static boolean isKnown(Artifact artifact, List knownArtifacts) { - return knownArtifacts.stream().anyMatch(artifactMatcherWithVersion(artifact)); - } - - private static void failOnVersionConflict(Artifact artifact, List knownArtifacts, String pluginArtifactId) throws MojoExecutionException { - Optional conflictingVersion = knownArtifacts.stream() - .filter(artifactMatcherWithoutVersion(artifact)) - .filter(artifactMatcherWithVersion(artifact).negate()) // same version -> not conflicting - .findFirst() - .map(Artifact::getVersion); - if (conflictingVersion.isPresent()) { - String artifactName = artifact.getGroupId() + artifact.getArtifactId(); - throw new MojoExecutionException("version conflict in " + pluginArtifactId + ": " + artifactName + " found in version " + artifact.getVersion() + " and version " + conflictingVersion.get()); - } - } - - private static void failUnlessComplete(List actualDependencies, List expectedDependencies, String pluginArtifactId) throws MojoExecutionException { - for (ExpectedDependency expected : expectedDependencies) { - if (actualDependencies.stream().noneMatch(expectedDependencyMatcher(expected))) { - String dependencyName = expected.groupId + ":" + expected.artifactId; - throw new MojoExecutionException("Plugin dependency " + dependencyName + " missing. This is a bug in " + pluginArtifactId + "."); - } - } - } -} diff --git a/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java b/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java deleted file mode 100644 index a124ca6..0000000 --- a/java_jar_agent/promagent-framework/promagent-maven-plugin/src/main/java/io/promagent/plugin/AgentJar.java +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2017 The Promagent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.promagent.plugin; - -import org.apache.maven.plugin.MojoExecutionException; -import org.codehaus.plexus.util.IOUtil; - -import java.io.*; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarOutputStream; - -class AgentJar implements AutoCloseable { - - private final String jarName; - private final JarOutputStream jarOutputStream; - private final Set content = new HashSet<>(); - - enum Directory { - SHARED_JARS("shared-jars/"), // Directory entries in JAR file must end in '/' - PER_DEPLOYMENT_JARS("per-deployment-jars/"); - - private final String name; - - Directory(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - private AgentJar(String jarName, JarOutputStream jarOutputStream) { - this.jarName = jarName; - this.jarOutputStream = jarOutputStream; - } - - static AgentJar create(File jarFile) throws MojoExecutionException { - try { - JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile))); - return new AgentJar(jarFile.getName(), jarOutputStream); - } catch (IOException e) { - throw new MojoExecutionException("Error creating " + jarFile.getName() + ": " + e.getMessage(), e); - } - } - - void addFile(File srcFile, String targetFileName, Directory targetDir) throws MojoExecutionException { - String destPath = targetDir.getName() + targetFileName; - if (content.contains(destPath)) { - return; - } - makeDirsRecursively(destPath); - content.add(destPath); - try (InputStream in = new FileInputStream(srcFile)) { - jarOutputStream.putNextEntry(new JarEntry(destPath)); - IOUtil.copy(in, jarOutputStream); - } catch (IOException e) { - throw new MojoExecutionException("Error adding " + srcFile.getName() + " to target JAR: " + e.getMessage(), e); - } - } - - void addFile(File srcFile, Directory targetDir) throws MojoExecutionException { - addFile(srcFile, srcFile.getName(), targetDir); - } - - void extractJar(File jar, ManifestTransformer manifestTransformer) throws MojoExecutionException { - try (JarFile jarFile = new JarFile(jar)) { - for (Enumeration jarEntries = jarFile.entries(); jarEntries.hasMoreElements(); ) { - JarEntry jarEntry = jarEntries.nextElement(); - if (manifestTransformer.canTransform(jarEntry)) { - jarEntry = manifestTransformer.transform(jarEntry); - } - if (!jarEntry.isDirectory() && !content.contains(jarEntry.getName())) { - content.add(jarEntry.getName()); - makeDirsRecursively(jarEntry.getName()); - try (InputStream in = getInputStream(jarEntry, jarFile, manifestTransformer)) { - jarOutputStream.putNextEntry(jarEntry); - IOUtil.copy(in, jarOutputStream); - } - } - } - } catch (IOException e) { - throw new MojoExecutionException("Error adding " + jar.getName() + " to target JAR: " + e.getMessage(), e); - } - } - - private InputStream getInputStream(JarEntry jarEntry, JarFile jarFile, ManifestTransformer manifestTransformer) throws IOException, MojoExecutionException { - InputStream in = jarFile.getInputStream(jarEntry); - if (manifestTransformer.canTransform(jarEntry)) { - in = manifestTransformer.transform(in); - } - return in; - } - - private void makeDirsRecursively(String path) throws MojoExecutionException { - String[] parts = path.split("/+"); - String segment = ""; - for (int i=0; i - 4.0.0 - - io.promagent - promagent-example - 1.0-SNAPSHOT - - promagent example - - jar - - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.0-SNAPSHOT - - - - - - io.promagent - promagent-api - ${promagent.framework.version} - provided - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - com.squareup.okhttp3 - okhttp - 3.11.0 - test - - - - - - promagent - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.1 - - - - - - io.promagent - promagent-maven-plugin - ${promagent.framework.version} - - - promagent - package - - build - - - - - - - - io.fabric8 - docker-maven-plugin - 0.27.2 - - true - - - - - ${project.artifactId}/oracle-jdk-8 - - ubuntu:17.10 - /opt - - apt-get update ; apt-get upgrade -y - apt-get install -y curl git neovim sudo - curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz - tar xfz jdk-8u192-linux-x64.tar.gz - curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz - tar xfz apache-maven-3.6.0-bin.tar.gz - - echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ - echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ - echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ - echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; - - adduser --disabled-password --gecos '' promagent - echo 'promagent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers - - promagent - - /opt/jdk1.8.0_192 - /opt/apache-maven-3.6.0 - /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH - - - - - - - ${project.artifactId}/wildfly-kitchensink - - ${project.artifactId}/oracle-jdk-8 - /home/promagent - - curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz - tar xfz wildfly-10.1.0.Final.tar.gz - rm wildfly-10.1.0.Final.tar.gz - git clone https://github.com/wildfly/quickstart.git - cd quickstart; git checkout 10.x - cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package - mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments - - mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments - rm -rf ./quickstart ; rm -rf ./.m2 - - - - - - - ${project.artifactId}/wildfly-kitchensink-promagent - - ${project.artifactId}/wildfly-kitchensink - - - echo '#!/bin/bash' >> run.sh ; \ - echo >> run.sh ; \ - echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ - echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ - echo 'export JAVA_OPTS="' >> run.sh ; \ - echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ - echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ - echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.promagent.agent' >> run.sh ; \ - echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ - echo ' -javaagent:../promagent.jar=port=9300' >> run.sh ; \ - echo '"' >> run.sh ; \ - echo >> run.sh ; \ - echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ - chmod 755 run.sh ; - - - - /home/promagent - promagent:promagent:promagent - - - - target/promagent.jar - / - - - - - - - - - - ${wildfly.port}:8080 - ${promagent.port}:9300 - - - WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms - - - ./run.sh - - true - [Wildfly] - - - - - - - ${project.artifactId}/spring - - ${project.artifactId}/oracle-jdk-8 - /home/promagent - - git clone https://github.com/spring-guides/gs-accessing-data-rest.git - cd gs-accessing-data-rest/complete; mvn --batch-mode package - mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . - rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 - - - - - - - ${project.artifactId}/spring-promagent - - ${project.artifactId}/spring - - - echo '#!/bin/bash' >> run.sh ; \ - echo >> run.sh ; \ - echo 'java -javaagent:promagent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ - chmod 755 run.sh ; - - - - /home/promagent - promagent:promagent:promagent - - - - target/promagent.jar - / - - - - - - - - - - ${spring.port}:8080 - ${promagent.port}:9300 - - - Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) - - - ./run.sh - - true - [Spring Boot] - - - - - - - - - start - pre-integration-test - - build - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - none - - skip-docker-tests - - - - wildfly - - - ${project.artifactId}/oracle-jdk-8, - ${project.artifactId}/wildfly-kitchensink, - ${project.artifactId}/wildfly-kitchensink-promagent - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/WildflyIT.java - - - - - http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink - http://${docker.host.address}:${promagent.port} - - - - - integration-tests - - integration-test - verify - - - - - - - - - spring - - - ${project.artifactId}/oracle-jdk-8, - ${project.artifactId}/spring, - ${project.artifactId}/spring-promagent - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/SpringIT.java - - - - - http://${docker.host.address}:${spring.port} - http://${docker.host.address}:${promagent.port} - - - - - integration-tests - - integration-test - verify - - - - - - - - - diff --git a/war_agent/pom.xml b/war_agent/pom.xml deleted file mode 100644 index 0ef05e6..0000000 --- a/war_agent/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - 4.0.0 - - io.promagent - promagent-example - 1.0-SNAPSHOT - - promagent example - - jar - - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.0-SNAPSHOT - - - - - - io.promagent - promagent-api - ${promagent.framework.version} - provided - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - com.squareup.okhttp3 - okhttp - 3.11.0 - test - - - - - - promagent - - - io.promagent - promagent-maven-plugin - ${promagent.framework.version} - - - promagent - package - - build - - - - - - - - diff --git a/war_agent/src/main/java/io/promagent/collectors/JmxCollector.java b/war_agent/src/main/java/io/promagent/collectors/JmxCollector.java deleted file mode 100644 index fa31f86..0000000 --- a/war_agent/src/main/java/io/promagent/collectors/JmxCollector.java +++ /dev/null @@ -1,35 +0,0 @@ - -package io.promagent.collectors; - -import com.sun.management.OperatingSystemMXBean; -import io.prometheus.client.Collector; - -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) -// TODO -- This class is not loaded by default, see commented-out lines in io.promagent.internal.Promagent.premain() -// See JmxCollector in jmx_exporter -public class JmxCollector extends Collector implements Collector.Describable { - - @Override - public List collect() { - List result = new ArrayList<>(); - result.add(collectOperatingSystemMetrics()); - return Collections.unmodifiableList(result); - } - - @Override - public List describe() { - return new ArrayList<>(); - } - - private MetricFamilySamples collectOperatingSystemMetrics() { - OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); - return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); - } -} diff --git a/war_agent/src/main/java/io/promagent/hooks/HttpContext.java b/war_agent/src/main/java/io/promagent/hooks/HttpContext.java deleted file mode 100644 index 7d8dccc..0000000 --- a/war_agent/src/main/java/io/promagent/hooks/HttpContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.promagent.hooks; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - - -/**Storing the http context in a thread to know which database request call was triggered. -*/ -class HttpContext { - - static class Key {} - - static final Key HTTP_METHOD = new Key<>(); - static final Key HTTP_PATH = new Key<>(); - - private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); - - static void put(Key key, T value) { - threadLocal.get().put(key, value); - } - - @SuppressWarnings("unchecked") - static Optional get(Key key) { - return Optional.ofNullable((T) threadLocal.get().get(key)); - } - - static void clear(Key... keys) { - for (Key key : keys) { - threadLocal.get().remove(key); - } - } -} diff --git a/war_agent/src/main/java/io/promagent/hooks/JdbcHook.java b/war_agent/src/main/java/io/promagent/hooks/JdbcHook.java deleted file mode 100644 index 675d449..0000000 --- a/war_agent/src/main/java/io/promagent/hooks/JdbcHook.java +++ /dev/null @@ -1,128 +0,0 @@ - -package io.promagent.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricDef; -import io.promagent.hookcontext.MetricsStore; -import io.prometheus.client.Counter; -import io.prometheus.client.Summary; - -import java.util.concurrent.TimeUnit; - -import static io.promagent.hooks.HttpContext.HTTP_METHOD; -import static io.promagent.hooks.HttpContext.HTTP_PATH; - -@Hook(instruments = { - "java.sql.Statement", - "java.sql.Connection" -}) -public class JdbcHook { - - private final Counter sqlQueriesTotal; - private final Summary sqlQueriesDuration; - private long startTime = 0; - - public JdbcHook(MetricsStore metricsStore) { - - sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( - "sql_queries_total", - (name, registry) -> Counter.build() - .name(name) - .labelNames("method", "path", "query") - .help("Total number of sql queries.") - .register(registry) - )); - - sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( - "sql_query_duration", - (name, registry) -> Summary.build() - .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error - .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error - .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error - .name(name) - .labelNames("method", "path", "query") - .help("Duration for serving the sql queries in seconds.") - .register(registry) - )); - } - - private String stripValues(String query) { - // We want the structure of the query as labels, not the actual values. - // Therefore, we replace: - // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') - // with - // insert into Member (id, name, email, phone_number) values (...) - return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); - } - - // --- before - - @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) - public void before(String sql) { - startTime = System.nanoTime(); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, int autoGeneratedKeys) { - before(sql); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, int[] columnIndexes) { - before(sql); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, String[] columnNames) { - before(sql); - } - - @Before(method = {"prepareStatement", "prepareCall"}) - public void before(String sql, int resultSetType, int resultSetConcurrency) { - before(sql); - } - - @Before(method = {"prepareStatement", "prepareCall"}) - public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { - before(sql); - } - - // --- after - - @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) - public void after(String sql) throws Exception { - double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); - String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); - String path = HttpContext.get(HTTP_PATH).orElse("no http context"); - String query = stripValues(sql); - sqlQueriesTotal.labels(method, path, query).inc(); - sqlQueriesDuration.labels(method, path, query).observe(duration); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, int autoGeneratedKeys) throws Exception { - after(sql); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, int[] columnIndexes) throws Exception { - after(sql); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, String[] columnNames) throws Exception { - after(sql); - } - - @After(method = {"prepareStatement", "prepareCall"}) - public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { - after(sql); - } - - @After(method = {"prepareStatement", "prepareCall"}) - public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { - after(sql); - } -} diff --git a/war_agent/src/main/java/io/promagent/hooks/ServletHook.java b/war_agent/src/main/java/io/promagent/hooks/ServletHook.java deleted file mode 100644 index e29e5ba..0000000 --- a/war_agent/src/main/java/io/promagent/hooks/ServletHook.java +++ /dev/null @@ -1,105 +0,0 @@ - - -package io.promagent.hooks; - -import io.promagent.annotations.After; -import io.promagent.annotations.Before; -import io.promagent.annotations.Hook; -import io.promagent.hookcontext.MetricDef; -import io.promagent.hookcontext.MetricsStore; -import io.prometheus.client.Counter; -import io.prometheus.client.Summary; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.concurrent.TimeUnit; - -import static io.promagent.hooks.HttpContext.HTTP_METHOD; -import static io.promagent.hooks.HttpContext.HTTP_PATH; - -@Hook(instruments = { - "javax.servlet.Servlet", - "javax.servlet.Filter" -}) -public class ServletHook { - - private final Counter httpRequestsTotal; - private final Summary httpRequestsDuration; - private long startTime = 0; - - public ServletHook(MetricsStore metricsStore) { - - httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( - "http_requests_total",https://github.com/asaini94/war_agent - (name, registry) -> Counter.build() - .name(name) - .labelNames("method", "path", "status") - .help("Total number of http requests.") - .register(registry) - )); - - httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( - "http_request_duration", - (name, registry) -> Summary.build() - .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error - .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error - .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error - .name(name) - .labelNames("method", "path", "status") - .help("Duration for serving the http requests in seconds.") - .register(registry) - )); - } - - private String stripPathParameters(String path) { - - // The URL path may include path parameters. - // For example, REST URLs for querying an item might look like this: - // - // /item/1 - // /item/2 - // /item/3 - // etc. - // - // /item/{id} - // - // This method replaces path parameters with placeholders. It is application specific and - // should be adapted depending on the actual paths in an application. - // For the demo, we just replace all numbers with {id}. - - return path - .replaceAll("/[0-9]+", "/{id}") - .replaceAll("/;jsessionid=\\w*", "") - .replaceAll("/$", "") - .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." - } - - @Before(method = {"service", "doFilter"}) - public void before(ServletRequest request, ServletResponse response) { - if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { - HttpServletRequest req = (HttpServletRequest) request; - HttpContext.put(HTTP_METHOD, req.getMethod()); - HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); - startTime = System.nanoTime(); - } - } - - // Return Werte und Exceptions als Parameter - @After(method = {"service", "doFilter"}) - public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { - if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { - HttpServletResponse resp = (HttpServletResponse) response; - try { - double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); - String method = HttpContext.get(HTTP_METHOD).get(); - String path = HttpContext.get(HTTP_PATH).get(); - httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); - httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); - } finally { - HttpContext.clear(HTTP_METHOD, HTTP_PATH); - } - } - } -} diff --git a/war_agent/src/test/java/io/promagent/it/SpringIT.java b/war_agent/src/test/java/io/promagent/it/SpringIT.java deleted file mode 100644 index f3ceec9..0000000 --- a/war_agent/src/test/java/io/promagent/it/SpringIT.java +++ /dev/null @@ -1,68 +0,0 @@ - - -package io.promagent.it; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.junit.jupiter.api.Test; - -public class SpringIT { - - /** - * Run some HTTP queries against a Docker container from image promagent/spring-promagent. - *

- * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. - */ - @Test - public void testSpring() throws Exception { - OkHttpClient client = new OkHttpClient(); - Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); - - // Execute two POST requests - Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); - assertEquals(201, restResponse.code()); - restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); - assertEquals(201, restResponse.code()); - - // Query Prometheus metrics from promagent - Response metricsResponse = client.newCall(metricsRequest).execute(); - String[] metricsLines = metricsResponse.body().string().split("\n"); - - String httpRequestsTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("http_requests_total")) - .filter(m -> m.contains("method=\"POST\"")) - .filter(m -> m.contains("path=\"/people\"")) - .filter(m -> m.contains("status=\"201\"")) - .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); - - assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); - - String sqlQueriesTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("sql_queries_total")) - // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: - // query="insert into person (first_name, last_name, id)" - .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) - .filter(m -> m.contains("method=\"POST\"")) - .filter(m -> m.contains("path=\"/people\"")) - .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); - - assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); - } - - private Request makePostRequest(String firstName, String lastName) { - return new Request.Builder() - .url(System.getProperty("deployment.url") + "/people") - .method("POST", RequestBody.create( - MediaType.parse("application/json"), - "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) - .build(); - } -} diff --git a/war_agent/src/test/java/io/promagent/it/WildflyIT.java b/war_agent/src/test/java/io/promagent/it/WildflyIT.java deleted file mode 100644 index 33b97a7..0000000 --- a/war_agent/src/test/java/io/promagent/it/WildflyIT.java +++ /dev/null @@ -1,67 +0,0 @@ - -package io.promagent.it; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class WildflyIT { - - /** - * Run some HTTP queries against a Docker container from image promagent/wildfly-kitchensink-promagent. - *

- * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. - */ - @Test - public void testWildfly() throws Exception { - OkHttpClient client = new OkHttpClient(); - Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); - - // Execute REST call - Response restResponse = client.newCall(restRequest).execute(); - Assertions.assertEquals(restResponse.code(), 200); - Assertions.assertTrue(restResponse.body().string().contains("John Smith")); - - Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric - assertMetrics(client, "1.0"); - - // Execute REST call again - restResponse = client.newCall(restRequest).execute(); - Assertions.assertEquals(restResponse.code(), 200); - Assertions.assertTrue(restResponse.body().string().contains("John Smith")); - - Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric - assertMetrics(client, "2.0"); - } - - private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { - - Request metricsRequest = new Request.Builder().url(System.getProperty("promagent.url") + "/metrics").build(); - Response metricsResponse = client.newCall(metricsRequest).execute(); - String[] metricsLines = metricsResponse.body().string().split("\n"); - - String httpRequestsTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("http_requests_total")) - .filter(m -> m.contains("method=\"GET\"")) - .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) - .filter(m -> m.contains("status=\"200\"")) - .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); - - assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); - - String sqlQueriesTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("sql_queries_total")) - .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) - .filter(m -> m.contains("method=\"GET\"")) - .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) - .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); - - assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); - } -} From 4ec3f8b480646311876e7b9cf7ac33d353df2afd Mon Sep 17 00:00:00 2001 From: anu Date: Sat, 16 Feb 2019 22:36:48 +0530 Subject: [PATCH 25/38] Added Spring boot Testing Env. for Agent --- MusicApp_springBoot/.gitignore | 25 ++++ MusicApp_springBoot/Dockerfile | 4 + MusicApp_springBoot/pom.xml | 115 ++++++++++++++++++ .../com/stackroute/MusicAppApplication.java | 16 +++ .../stackroute/config/ApplicationStartup.java | 60 +++++++++ .../com/stackroute/config/SwaggerConfig.java | 36 ++++++ .../controller/TrackController.java | 95 +++++++++++++++ .../java/com/stackroute/domain/Track.java | 31 +++++ .../com/stackroute/error/ErrorResponse.java | 18 +++ .../exception/GlobalExceptionHandler.java | 36 ++++++ .../TrackAlreadyExistsException.java | 13 ++ .../exception/TrackDoesNotExistException.java | 14 +++ .../repository/TrackRepository.java | 26 ++++ .../com/stackroute/service/TrackService.java | 22 ++++ .../stackroute/service/TrackServiceImpl.java | 84 +++++++++++++ .../src/main/resources/application.properties | 6 + .../src/main/resources/application.yml | 29 +++++ .../src/main/resources/bootstrap.properties | 2 + .../src/main/resources/seed.properties | 7 ++ .../stackroute/MusicAppApplicationTests.java | 17 +++ 20 files changed, 656 insertions(+) create mode 100644 MusicApp_springBoot/.gitignore create mode 100644 MusicApp_springBoot/Dockerfile create mode 100644 MusicApp_springBoot/pom.xml create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/MusicAppApplication.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/config/ApplicationStartup.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/config/SwaggerConfig.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/controller/TrackController.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/domain/Track.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/error/ErrorResponse.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/exception/GlobalExceptionHandler.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackAlreadyExistsException.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackDoesNotExistException.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/repository/TrackRepository.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/service/TrackService.java create mode 100644 MusicApp_springBoot/src/main/java/com/stackroute/service/TrackServiceImpl.java create mode 100644 MusicApp_springBoot/src/main/resources/application.properties create mode 100644 MusicApp_springBoot/src/main/resources/application.yml create mode 100644 MusicApp_springBoot/src/main/resources/bootstrap.properties create mode 100644 MusicApp_springBoot/src/main/resources/seed.properties create mode 100644 MusicApp_springBoot/src/test/java/com/stackroute/MusicAppApplicationTests.java diff --git a/MusicApp_springBoot/.gitignore b/MusicApp_springBoot/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/MusicApp_springBoot/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/MusicApp_springBoot/Dockerfile b/MusicApp_springBoot/Dockerfile new file mode 100644 index 0000000..ca64cae --- /dev/null +++ b/MusicApp_springBoot/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:11.0-oracle +ADD ./target/MusicApp-0.0.1-SNAPSHOT.jar /usr/src/MusicApp-0.0.1-SNAPSHOT.jar +WORKDIR usr/src +ENTRYPOINT ["java","-jar","MusicApp-0.0.1-SNAPSHOT.jar"] \ No newline at end of file diff --git a/MusicApp_springBoot/pom.xml b/MusicApp_springBoot/pom.xml new file mode 100644 index 0000000..0a19999 --- /dev/null +++ b/MusicApp_springBoot/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.stackroute + MusicApp + 0.0.1-SNAPSHOT + MusicApp + Music Application + + + 11 + Greenwich.RC2 + + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-data-rest + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.projectlombok + lombok + 1.18.2 + provided + + + + io.springfox + springfox-swagger2 + 2.6.1 + compile + + + io.springfox + springfox-swagger-ui + 2.6.1 + compile + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + org.springframework.boot + spring-boot-devtools + 2.1.2.RELEASE + + + org.springframework.cloud + spring-cloud-starter-config + 1.1.2.RELEASE + + + + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + + + diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/MusicAppApplication.java b/MusicApp_springBoot/src/main/java/com/stackroute/MusicAppApplication.java new file mode 100644 index 0000000..d4042cf --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/MusicAppApplication.java @@ -0,0 +1,16 @@ +package com.stackroute; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@EnableEurekaClient +@SpringBootApplication +public class MusicAppApplication { + + public static void main(String[] args) { + SpringApplication.run(MusicAppApplication.class, args); + } + +} + diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/config/ApplicationStartup.java b/MusicApp_springBoot/src/main/java/com/stackroute/config/ApplicationStartup.java new file mode 100644 index 0000000..e75888c --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/config/ApplicationStartup.java @@ -0,0 +1,60 @@ +package com.stackroute.config; + +import com.stackroute.domain.Track; +import com.stackroute.repository.TrackRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +//Approach-One using Application Listener +@Configuration +@PropertySource("classpath:seed.properties") +@Component +public class ApplicationStartup implements ApplicationListener { + +//Approach-Two using CommandLine Runner +//public class ApplicationStartup implements CommandLineRunner{ + + @Value("${1trackId}") + private int trackId1; + @Value("${1trackName}") + private String trackName1; + @Value("${1trackComment}") + private String trackComment1; + @Value("${2trackId}") + private int trackId2; + @Value("${2trackName}") + private String trackName2; + @Value("${2trackComment}") + private String trackComment2; + + + + @Autowired + private TrackRepository trackRepository; + +//Method to be over-ridden for applicationListener + @Override + public void onApplicationEvent(final ApplicationReadyEvent applicationReadyEvent) { + seedData(); + } + +//Method to be over-ridden for command line runner +// @Override +// public void run(String... args) throws Exception { +// seedData(); +// } + + private void seedData() { + trackRepository.save(new Track(trackId1,trackName1,trackComment1)); + trackRepository.save(new Track(trackId2,trackName2,trackComment2)); + } + +} + + diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/config/SwaggerConfig.java b/MusicApp_springBoot/src/main/java/com/stackroute/config/SwaggerConfig.java new file mode 100644 index 0000000..1e79dce --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/config/SwaggerConfig.java @@ -0,0 +1,36 @@ +package com.stackroute.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +import static springfox.documentation.builders.PathSelectors.regex; + +@Configuration +@EnableSwagger2 +public class SwaggerConfig { + @Bean + public Docket productApi() { + return new Docket(DocumentationType.SWAGGER_2) + .select().apis(RequestHandlerSelectors.basePackage("com.stackroute.controller")) + .paths(regex("/api/v3.*")) + .build().apiInfo(metaData()); + } + + private ApiInfo metaData() { + ApiInfo apiInfo = new ApiInfo( + "Music Application-v3", + "Database used is MongoDB", + "1.0", + "Terms of service", + new Contact("Shri Ramya", "", "shriramyaudupa3996@gmail.com"), + "Apache License Version 2.0", + "https://www.apache.org/licenses/LICENSE-2.0"); + return apiInfo; + } +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/controller/TrackController.java b/MusicApp_springBoot/src/main/java/com/stackroute/controller/TrackController.java new file mode 100644 index 0000000..87e99e4 --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/controller/TrackController.java @@ -0,0 +1,95 @@ +package com.stackroute.controller; + +import com.stackroute.domain.Track; +import com.stackroute.exception.TrackAlreadyExistsException; +import com.stackroute.exception.TrackDoesNotExistException; +import com.stackroute.service.TrackService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping(value = "api/v3/") +public class TrackController { + + private TrackService trackService; + + @Autowired + public TrackController(TrackService musicService) { + this.trackService = musicService; + } + + @ApiOperation(value = "Saves a valid track" ) + @PostMapping(value = "track") + public ResponseEntity saveTrack(@RequestBody Track track) throws TrackAlreadyExistsException { + + ResponseEntity responseEntity; + + Track track1= trackService.saveTrack(track) ; + responseEntity = new ResponseEntity("Successfully saved!", HttpStatus.CREATED); + + return responseEntity; + } + + @ApiOperation(value = "Returns all the tracks saved") + @GetMapping(value = "tracks") + public ResponseEntity getAllTracks() throws TrackDoesNotExistException { + ResponseEntity responseEntity; + + + responseEntity = new ResponseEntity>(trackService.displayTracks(), HttpStatus.OK); + + return responseEntity; + } + + @ApiOperation(value = "Deletes a track of given id if it exists") + @DeleteMapping(value = "track/{id}") + public ResponseEntity deleteTrack(@PathVariable(value = "id") int id) throws TrackDoesNotExistException{ + ResponseEntity responseEntity; + + responseEntity = new ResponseEntity(trackService.removeTrack(id), HttpStatus.OK); + + return responseEntity; + } + + @ApiOperation(value = "Updates the comment of a track") + @PutMapping(value = "track") + public ResponseEntity updateComment(@RequestBody Track track) throws TrackDoesNotExistException{ + ResponseEntity responseEntity; + + Track music1= trackService.updateComment(track); + responseEntity = new ResponseEntity(music1, HttpStatus.ACCEPTED); + + return responseEntity; + } + + @ApiOperation(value = "Returns all the tracks of given name") + @GetMapping(value = "track/{trackName}") + public ResponseEntity findTrackByTrackName(@PathVariable(value = "trackName") String trackName) throws TrackDoesNotExistException{ + ResponseEntity responseEntity; + + List trackList=trackService.displayTrackByName(trackName); + responseEntity=new ResponseEntity>(trackList,HttpStatus.OK); + + return responseEntity; + } +// +// @GetMapping(value = "track/id/{trackId}") +// public ResponseEntity displayWithIdMoreThan(@PathVariable(value = "trackId") int trackId){ +// ResponseEntity responseEntity; +// +// try{ +// List trackList=trackService.displayTrackWithIdGreaterThan(trackId); +// responseEntity=new ResponseEntity>(trackList,HttpStatus.OK); +// }catch (TrackDoesNotExistException e){ +// responseEntity=new ResponseEntity(e.getMessage(),HttpStatus.GONE); +// } +// return responseEntity; +// +// } +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/domain/Track.java b/MusicApp_springBoot/src/main/java/com/stackroute/domain/Track.java new file mode 100644 index 0000000..7ff1323 --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/domain/Track.java @@ -0,0 +1,31 @@ +package com.stackroute.domain; + +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; + + +import javax.annotation.processing.Generated; +import java.lang.annotation.Documented; +import java.util.Objects; + +@Document +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class Track { + + @Id + @ApiModelProperty(notes = "Track's Id that is unique") + private int trackId; + @ApiModelProperty(notes = "Track's name") + private String trackName; + @ApiModelProperty(notes = "Track's comment that would help other listeners to judge the song") + private String trackComment; + +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/error/ErrorResponse.java b/MusicApp_springBoot/src/main/java/com/stackroute/error/ErrorResponse.java new file mode 100644 index 0000000..bc4d83c --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/error/ErrorResponse.java @@ -0,0 +1,18 @@ +package com.stackroute.error; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ErrorResponse { + + + private int errorCode; + private String errorMessage; +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/exception/GlobalExceptionHandler.java b/MusicApp_springBoot/src/main/java/com/stackroute/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..05828fa --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/exception/GlobalExceptionHandler.java @@ -0,0 +1,36 @@ +package com.stackroute.exception; + +import com.stackroute.error.ErrorResponse; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; + + +@ControllerAdvice +public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { + + @ExceptionHandler(TrackAlreadyExistsException.class) + public ResponseEntity trackExists(TrackAlreadyExistsException ex) throws Exception{ + + ErrorResponse errorResponse = new ErrorResponse(); + errorResponse.setErrorCode(HttpStatus.CONFLICT.value()); + errorResponse.setErrorMessage(ex.getMessage()); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + + } + + @ExceptionHandler(TrackDoesNotExistException.class) + public ResponseEntity trackDoesNotExist(TrackDoesNotExistException ex) throws Exception{ + + ErrorResponse errorResponse = new ErrorResponse(); + errorResponse.setErrorCode(HttpStatus.NOT_FOUND.value()); + errorResponse.setErrorMessage(ex.getMessage()); + return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); + + } + + + +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackAlreadyExistsException.java b/MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackAlreadyExistsException.java new file mode 100644 index 0000000..b29d2c8 --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackAlreadyExistsException.java @@ -0,0 +1,13 @@ +package com.stackroute.exception; + +public class TrackAlreadyExistsException extends Exception { + + private String message; + + public TrackAlreadyExistsException(){} + + public TrackAlreadyExistsException(String message) { + super(message); + this.message = message; + } +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackDoesNotExistException.java b/MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackDoesNotExistException.java new file mode 100644 index 0000000..8d323b0 --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/exception/TrackDoesNotExistException.java @@ -0,0 +1,14 @@ +package com.stackroute.exception; + +public class TrackDoesNotExistException extends Exception{ + + private String message; + + public TrackDoesNotExistException() { + } + + public TrackDoesNotExistException(String message) { + super(message); + this.message = message; + } +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/repository/TrackRepository.java b/MusicApp_springBoot/src/main/java/com/stackroute/repository/TrackRepository.java new file mode 100644 index 0000000..588062e --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/repository/TrackRepository.java @@ -0,0 +1,26 @@ +package com.stackroute.repository; + +import com.stackroute.domain.Track; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.data.repository.query.Param; + +import java.util.List; + + +public interface TrackRepository extends MongoRepository { + + List findByTrackName(String trackName); + + +// @Query(value = "select t from Track t where t.trackId>?1") +// List findTrackWithCondition(int trackId); + + +// Same query can also be written in this way where we use named parameters. We have multiple ways where +// we can define our custom queries and pass parameters to them. +// +// @Query(value = "select t from Track t where t.trackId>:trackId") +// List findTrackWithCondition(@Param(value = "trackId") int trackId); + + +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/service/TrackService.java b/MusicApp_springBoot/src/main/java/com/stackroute/service/TrackService.java new file mode 100644 index 0000000..14e2fd1 --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/service/TrackService.java @@ -0,0 +1,22 @@ +package com.stackroute.service; + +import com.stackroute.domain.Track; +import com.stackroute.exception.TrackAlreadyExistsException; +import com.stackroute.exception.TrackDoesNotExistException; + +import java.util.List; + +public interface TrackService { + + public Track saveTrack(Track track) throws TrackAlreadyExistsException; + + public List displayTracks() throws TrackDoesNotExistException; + + public Track updateComment(Track track) throws TrackDoesNotExistException; + + public Track removeTrack(int id) throws TrackDoesNotExistException; + + public List displayTrackByName(String trackName) throws TrackDoesNotExistException; + +// public List displayTrackWithIdGreaterThan(int trackId) throws TrackDoesNotExistException; +} diff --git a/MusicApp_springBoot/src/main/java/com/stackroute/service/TrackServiceImpl.java b/MusicApp_springBoot/src/main/java/com/stackroute/service/TrackServiceImpl.java new file mode 100644 index 0000000..faf047c --- /dev/null +++ b/MusicApp_springBoot/src/main/java/com/stackroute/service/TrackServiceImpl.java @@ -0,0 +1,84 @@ +package com.stackroute.service; + +import com.stackroute.domain.Track; +import com.stackroute.exception.TrackAlreadyExistsException; +import com.stackroute.exception.TrackDoesNotExistException; +import com.stackroute.repository.TrackRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.util.List; + +@Service +public class TrackServiceImpl implements TrackService { + + TrackRepository trackRepository; + + @Autowired + public TrackServiceImpl(TrackRepository trackRepository) { + this.trackRepository = trackRepository; + } + + @Override + public Track saveTrack(Track track) throws TrackAlreadyExistsException { + Track savedTrack=null; + if(trackRepository.existsById(track.getTrackId())){ + throw new TrackAlreadyExistsException("Track already exists"); + } + savedTrack = trackRepository.save(track); + if(savedTrack==null){ + throw new TrackAlreadyExistsException("Track already exists"); + } + return savedTrack; + } + + @Override + public List displayTracks() throws TrackDoesNotExistException { + List trackList= trackRepository.findAll(); + if (trackList.isEmpty()){ + throw new TrackDoesNotExistException("Your TrackList is Empty"); + } + return trackList; + } + + @Override + public Track updateComment(Track track) throws TrackDoesNotExistException { + Track returnTrackObj=null; + if (trackRepository.existsById(track.getTrackId())){ + returnTrackObj= trackRepository.save(track); + }else{ + throw new TrackDoesNotExistException("Track does not exist."); + } + return returnTrackObj; + } + + @Override + public Track removeTrack(int id) throws TrackDoesNotExistException { + boolean flag=false; + Track returnTrack; + if(trackRepository.existsById(id)){ + returnTrack=trackRepository.findById(id).get(); + trackRepository.deleteById(id); + }else{ + throw new TrackDoesNotExistException("Track does not exist."); + } + return returnTrack; + } + + @Override + public List displayTrackByName(String trackName) throws TrackDoesNotExistException { + List trackList=trackRepository.findByTrackName(trackName); + if (trackList.isEmpty()){ + throw new TrackDoesNotExistException("Track with \""+ trackName+"\" Track-Name, does not exist"); + } + return trackList; + } +// +// @Override +// public List displayTrackWithIdGreaterThan(int trackId) throws TrackDoesNotExistException { +// List trackList=trackRepository.findTrackWithCondition(trackId); +// if (trackList.isEmpty()){ +// throw new TrackDoesNotExistException("Track/s with trackId greater than "+trackId+" does not exist"); +// } +// return trackList; +// } +} diff --git a/MusicApp_springBoot/src/main/resources/application.properties b/MusicApp_springBoot/src/main/resources/application.properties new file mode 100644 index 0000000..92f2c7b --- /dev/null +++ b/MusicApp_springBoot/src/main/resources/application.properties @@ -0,0 +1,6 @@ +##Local MongoDB config +#spring.data.mongodb.authentication-database=admin +#spring.data.mongodb.database=track +#spring.data.mongodb.port=27017 +#spring.data.mongodb.host=172.23.239.122 +#server.port=8098 \ No newline at end of file diff --git a/MusicApp_springBoot/src/main/resources/application.yml b/MusicApp_springBoot/src/main/resources/application.yml new file mode 100644 index 0000000..493e861 --- /dev/null +++ b/MusicApp_springBoot/src/main/resources/application.yml @@ -0,0 +1,29 @@ + + + +eureka: #tells about the Eureka server details and its refresh time + instance: + leaseRenewalIntervalInSeconds: 1 + leaseExpirationDurationInSeconds: 2 + client: + serviceUrl: + defaultZone: http://127.0.0.1:8761/eureka/ + healthcheck: + enabled: true + lease: + duration: 5 + + +# +#spring: +# application: +# name: music-service +# #current service name to be used by the eureka server +# +#management: +# security: +# enabled: false #disable the spring security on the management endpoints like /env, /refresh etc. + +#logging: +# level: +# com.example.howtodoinjava: DEBUG \ No newline at end of file diff --git a/MusicApp_springBoot/src/main/resources/bootstrap.properties b/MusicApp_springBoot/src/main/resources/bootstrap.properties new file mode 100644 index 0000000..d57552a --- /dev/null +++ b/MusicApp_springBoot/src/main/resources/bootstrap.properties @@ -0,0 +1,2 @@ +spring.application.name= music-service +spring.cloud.config.uri= http://localhost:8088 diff --git a/MusicApp_springBoot/src/main/resources/seed.properties b/MusicApp_springBoot/src/main/resources/seed.properties new file mode 100644 index 0000000..f54fcf3 --- /dev/null +++ b/MusicApp_springBoot/src/main/resources/seed.properties @@ -0,0 +1,7 @@ +1trackId=1 +1trackName=Worth It +1trackComment=Listen! It's Worth it! + +2trackId=2 +2trackName=Maa ka phone aaya +2trackComment=Epic song for custom ringtone;) \ No newline at end of file diff --git a/MusicApp_springBoot/src/test/java/com/stackroute/MusicAppApplicationTests.java b/MusicApp_springBoot/src/test/java/com/stackroute/MusicAppApplicationTests.java new file mode 100644 index 0000000..ba205a8 --- /dev/null +++ b/MusicApp_springBoot/src/test/java/com/stackroute/MusicAppApplicationTests.java @@ -0,0 +1,17 @@ +package com.stackroute; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class MusicAppApplicationTests { + + @Test + public void contextLoads() { + } + +} + From e7f1ef88a4f71df0d73d52a3478a8370e8857068 Mon Sep 17 00:00:00 2001 From: anu Date: Mon, 18 Feb 2019 00:02:14 +0530 Subject: [PATCH 26/38] Added java(.war and .jar) agent code --- .../agent-framework/agent-agent/pom.xml | 42 ++ .../java/io/agent/agent/ClassLoaderCache.java | 65 +++ .../main/java/io/agent/agent/JarFiles.java | 127 ++++++ .../agent/agent/PerDeploymentClassLoader.java | 43 ++ .../src/main/java/io/agent/agent/agent.java | 20 + .../java/io/agent/agent/JarFilesTest.java | 59 +++ .../agent-framework/agent-api/pom.xml | 21 + .../main/java/io/agent/annotations/After.java | 17 + .../java/io/agent/annotations/Before.java | 17 + .../main/java/io/agent/annotations/Hook.java | 29 ++ .../java/io/agent/annotations/Returned.java | 18 + .../java/io/agent/annotations/Thrown.java | 33 ++ .../java/io/agent/hookcontext/MetricDef.java | 38 ++ .../io/agent/hookcontext/MetricsStore.java | 38 ++ .../agent-framework/agent-exporter/pom.xml | 31 ++ .../agent/exporter/agentExporterServlet.java | 29 ++ .../agent-framework/agent-internal/pom.xml | 69 ++++ .../java/io/agent/internal/BuiltInServer.java | 57 +++ .../java/io/agent/internal/Delegator.java | 264 ++++++++++++ .../java/io/agent/internal/HookException.java | 12 + .../java/io/agent/internal/HookInstance.java | 20 + .../java/io/agent/internal/HookMetadata.java | 128 ++++++ .../io/agent/internal/HookMetadataParser.java | 321 +++++++++++++++ .../main/java/io/agent/internal/agent.java | 141 +++++++ .../java/io/agent/internal/agentAdvice.java | 64 +++ .../java/io/agent/internal/jmx/Exporter.java | 27 ++ .../io/agent/internal/jmx/ExporterMBean.java | 5 + .../java/io/agent/internal/jmx/Metric.java | 35 ++ .../io/agent/internal/jmx/MetricMBean.java | 16 + .../internal/jmx/agentCollectorRegistry.java | 52 +++ .../agent-framework/agent-loader/pom.xml | 47 +++ .../java/io/agent/loader/agentLoader.java | 87 ++++ .../agent-maven-plugin/pom.xml | 73 ++++ .../io/agent/plugin/AgentDependencies.java | 165 ++++++++ .../main/java/io/agent/plugin/AgentJar.java | 123 ++++++ .../java/io/agent/plugin/JarFileNames.java | 63 +++ .../io/agent/plugin/ManifestTransformer.java | 51 +++ .../main/java/io/agent/plugin/agentMojo.java | 50 +++ war&&jar_agent/agent-framework/pom.xml | 85 ++++ .../war_agent/pom-with-docker-tests.xml | 386 ++++++++++++++++++ war&&jar_agent/war_agent/pom.xml | 76 ++++ .../io/agent/collectors/JmxCollector.java | 36 ++ .../main/java/io/agent/hooks/HttpContext.java | 33 ++ .../main/java/io/agent/hooks/JdbcHook.java | 129 ++++++ .../main/java/io/agent/hooks/ServletHook.java | 108 +++++ .../src/test/java/io/agent/it/SpringIT.java | 80 ++++ .../src/test/java/io/agent/it/WildflyIT.java | 80 ++++ 47 files changed, 3510 insertions(+) create mode 100644 war&&jar_agent/agent-framework/agent-agent/pom.xml create mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java create mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/JarFiles.java create mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java create mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java create mode 100644 war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java create mode 100644 war&&jar_agent/agent-framework/agent-api/pom.xml create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java create mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java create mode 100644 war&&jar_agent/agent-framework/agent-exporter/pom.xml create mode 100644 war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/pom.xml create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java create mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java create mode 100644 war&&jar_agent/agent-framework/agent-loader/pom.xml create mode 100644 war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java create mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml create mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java create mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java create mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/JarFileNames.java create mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/ManifestTransformer.java create mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/agentMojo.java create mode 100644 war&&jar_agent/agent-framework/pom.xml create mode 100644 war&&jar_agent/war_agent/pom-with-docker-tests.xml create mode 100644 war&&jar_agent/war_agent/pom.xml create mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java create mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java create mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java create mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java create mode 100644 war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java create mode 100644 war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java diff --git a/war&&jar_agent/agent-framework/agent-agent/pom.xml b/war&&jar_agent/agent-framework/agent-agent/pom.xml new file mode 100644 index 0000000..97d4714 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-agent/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-agent + agent-agent loaded from the system class loader + + jar + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + io.agent.agent.agent + io.agent.agent.agent + true + true + true + + + + + + + diff --git a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java new file mode 100644 index 0000000..70a70b7 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java @@ -0,0 +1,65 @@ +package io.agent.agent; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * ClassLoaderCache stores the class loaders used for loading the agent modules, + * i.e. agent-hooks, agent-annotations, agent-internals, and their dependencies. + *

+ * For the Hooks (like ServletHook or JdbcHook) there is one class loader per deployment, + * because hook classes may reference classes from the deployment, + * e.g. as parameters to the before() and after() methods. + * All other modules and their dependencies are loaded through a shared class loader. + *

+ */ +public class ClassLoaderCache { + + private static ClassLoaderCache instance; + + // TODO: The cache does not free class loaders when applications are undeployed. Maybe use WeakHashMap? + private final Map cache = new HashMap<>(); + private final URLClassLoader sharedClassLoader; // shared across multiple deployments + private final List perDeploymentJars; // one class loader for each deployment for these JARs + + private ClassLoaderCache(JarFiles jarFiles) { + sharedClassLoader = new URLClassLoader(pathsToURLs(jarFiles.getSharedJars())); + perDeploymentJars = jarFiles.getPerDeploymentJars(); + } + + public static synchronized ClassLoaderCache getInstance() { + if (instance == null) { + instance = new ClassLoaderCache(JarFiles.extract()); + } + return instance; + } + + public List getPerDeploymentJars() { + return perDeploymentJars; + } + + public synchronized ClassLoader currentClassLoader() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (! cache.containsKey(contextClassLoader)) { + cache.put(contextClassLoader, new PerDeploymentClassLoader(pathsToURLs(perDeploymentJars), sharedClassLoader, contextClassLoader)); + } + return cache.get(contextClassLoader); + } + + private static URL[] pathsToURLs(List paths) { + try { + URL[] result = new URL[paths.size()]; + for (int i=0; i perDeploymentJars; // classes loaded for each deployment + private final List sharedJars; // classes shared across multiple deployments + + private JarFiles(List perDeploymentJars, List sharedJars) { + this.perDeploymentJars = Collections.unmodifiableList(perDeploymentJars); + this.sharedJars = Collections.unmodifiableList(sharedJars); + } + + List getPerDeploymentJars() { + return perDeploymentJars; + } + + List getSharedJars() { + return sharedJars; + } + + /** + * Theoretically we could return a list of jar:/ URLs without extracting the JARs, + * but the URLClassLoader has a bug such that jar:/ URLs cannot be used. Therefore, we have + * to extract the JARs and return a list of file:/ URLs. + * See https://bugs.openjdk.java.net/browse/JDK-4735639 + */ + static JarFiles extract() { + List perDeploymentJars = new ArrayList<>(); + List sharedJars = new ArrayList<>(); + Path agentJar = findAgentJar(); + List extractedJars; + try { + Path tmpDir = Files.createTempDirectory("agent-"); + tmpDir.toFile().deleteOnExit(); + extractedJars = unzip(agentJar, tmpDir, entry -> entry.getName().endsWith(".jar")); + } catch (IOException e) { + throw new RuntimeException("Failed to load agent.jar: " + e.getMessage(), e); + } + for (Path jar : extractedJars) { + if (jar.getParent().getFileName().toString().equals("per-deployment-jars")) { + perDeploymentJars.add(jar); + } else { + sharedJars.add(jar); + } + } + return new JarFiles(perDeploymentJars, sharedJars); + } + + private static List unzip(Path jarFile, Path destDir, Predicate filter) throws IOException { + List result = new ArrayList<>(); + try (JarFile agentJar = new JarFile(jarFile.toFile())) { + Enumeration jarEntries = agentJar.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry jarEntry = jarEntries.nextElement(); + if (filter.test(jarEntry)) { + Path destFile = destDir.resolve(jarEntry.getName()); + if (!destFile.getParent().toFile().exists()) { + if (!destFile.getParent().toFile().mkdirs()) { + throw new IOException("Failed to make directory: " + destFile.getParent()); + } + } + Files.copy(agentJar.getInputStream(jarEntry), destFile); + result.add(destFile); + } + } + } + return result; + } + + private static Path findAgentJar() { + CodeSource cs = agent.class.getProtectionDomain().getCodeSource(); + if (cs != null) { + return findAgentJarFromCodeSource(cs); + } else { + // This happens if the agent class is loaded from the bootstrap class loader, + // i.e. in addition to the command line argument -javaagent:/path/to/agent.jar, + // the argument -Xbootclasspath/p:/path/to/agent.jar is used. + return findAgentJarFromCmdline(ManagementFactory.getRuntimeMXBean().getInputArguments()); + } + } + + private static Path findAgentJarFromCodeSource(CodeSource cs) { + try { + return Paths.get(cs.getLocation().toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException("Failed to load agent.jar from " + cs.getLocation() + ": " + e.getMessage(), e); + } + } + + static Path findAgentJarFromCmdline(List cmdlineArgs) { + Pattern p = Pattern.compile("^-javaagent:(.*agent([^/]*).jar)(=.*)?$"); + for (String arg : cmdlineArgs) { + Matcher m = p.matcher(arg); + if (m.matches()) { + return Paths.get(m.group(1)); + } + } + throw new RuntimeException("Failed to locate agent.jar file."); + } +} diff --git a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java new file mode 100644 index 0000000..7f1ae8f --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java @@ -0,0 +1,43 @@ +package io.agent.agent; + +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Class loader for loading Hooks (like ServletHook or JdbcHook). + *

+ * There is one instance per deployment in an application server, because hook classes may + * reference classes from the deployment, e.g. as parameters to the before() and after() methods. + *

+ * However, loading shared classes like the Prometheus client library is delegated to the {@link #sharedClassLoader}, + * because the Prometheus metric registry should be accessible across all deployments within an application server. + */ +class PerDeploymentClassLoader extends URLClassLoader { + + private final URLClassLoader sharedClassLoader; // for loading the Prometheus client library + + PerDeploymentClassLoader(URL[] perDeploymentJars, URLClassLoader sharedClassLoader, ClassLoader parent) { + super(perDeploymentJars, parent); + this.sharedClassLoader = sharedClassLoader; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + return sharedClassLoader.loadClass(name); // The Prometheus client library should all have the same initiating loader across deployments. + } catch (ClassNotFoundException e) { + return super.loadClass(name); // Hooks should have different initiating loaders if the context loader differs. + } + } + + // Called by Byte buddy to load the agentAdvice. + @Override + public InputStream getResourceAsStream(String name) { + InputStream result = sharedClassLoader.getResourceAsStream(name); + if (result == null) { + result = super.getResourceAsStream(name); + } + return result; + } +} diff --git a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java new file mode 100644 index 0000000..f687262 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java @@ -0,0 +1,20 @@ +package io.agent.agent; + +import java.lang.instrument.Instrumentation; + +/** + * We want as little dependencies as possible on the system class loader, + * so the actual agent is loaded in its own class loader and this agent delegates to it. + */ +public class agent { + + public static void premain(String agentArgs, Instrumentation inst) throws Exception { + ClassLoader agentClassLoader = ClassLoaderCache.getInstance().currentClassLoader(); + Class agentClass = agentClassLoader.loadClass("io.agent.internal.agent"); + agentClass.getMethod("premain", String.class, Instrumentation.class).invoke(null, agentArgs, inst); + } + + public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { + premain(agentArgs, inst); + } +} diff --git a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java b/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java new file mode 100644 index 0000000..0fdac25 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java @@ -0,0 +1,59 @@ +// Copyright 2017 The agent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.agent.agent; + +import org.junit.jupiter.api.Test; + +import static io.agent.agent.JarFiles.findAgentJarFromCmdline; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; + +class JarFilesTest { + + @Test + void testCmdlineParserWildfly() { + // The command line arguments are taken from the Wildfly application server example. + String[] cmdlineArgs = new String[]{ + "-D[Standalone]", + "-Xbootclasspath/p:/tmp/wildfly-10.1.0.Final/modules/system/layers/base/org/jboss/logmanager/main/jboss-logmanager-2.0.4.Final.jar", + "-Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent", + "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", + "-javaagent:../agent/agent-dist/target/agent.jar=port=9300", + "-Dorg.jboss.boot.log.file=/tmp/wildfly-10.1.0.Final/standalone/log/server.log", + "-Dlogging.configuration=file:/tmp/wildfly-10.1.0.Final/standalone/configuration/logging.properties" + }; + assertEquals("../agent/agent-dist/target/agent.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); + } + + @Test + void testCmdlineParserVersioned() { + String[] cmdlineArgs = new String[] { + "-javaagent:agent-1.0-SNAPSHOT.jar" + }; + assertEquals("agent-1.0-SNAPSHOT.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); + } + + @Test() + void testCmdlineParserFailed() { + String[] cmdlineArgs = new String[] { + "-javaagent:/some/other/agent.jar", + "-jar", + "agent.jar" + }; + Exception e = assertThrows(Exception.class, () -> findAgentJarFromCmdline(asList(cmdlineArgs))); + // The exception should contain some message indicating agent.jar was not found. + assertTrue(e.getMessage().contains("agent.jar")); + } +} diff --git a/war&&jar_agent/agent-framework/agent-api/pom.xml b/war&&jar_agent/agent-framework/agent-api/pom.xml new file mode 100644 index 0000000..d034433 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-api + agent-api API for implementing hooks + + jar + + + + io.prometheus + simpleclient_common + + + diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java new file mode 100644 index 0000000..c3f73e4 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java @@ -0,0 +1,17 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The method annotated with @After is executed before exiting the instrumented method. + * The method annotated with @After must have exactly the same parameters as the instrumented method. + * The "method" parameter are the names of the instrumented methods. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface After { + String[] method(); +} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java new file mode 100644 index 0000000..d3cab3b --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java @@ -0,0 +1,17 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The method annotated with @Before is executed before entering the instrumented method. + * The method annotated with @Before must have exactly the same parameters as the instrumented method. + * The "method" parameter are the names of the instrumented methods. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Before { + String[] method(); +} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java new file mode 100644 index 0000000..a1b0622 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java @@ -0,0 +1,29 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The @Hook annotation indicates that the annotated class is a Hook. + * The parameter {@link Hook#instruments()} defines which classes or interfaces are instrumented by that Hook. + * The method annotations {@link Before} and {@link After} define which methods should be instrumented within that class or interface. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Hook { + + /** + * List of classes or interfaces to be instrumented. + */ + String[] instruments(); + + /** + * If true, nested calls are skipped. + * If set to {@code false}, nested calls will also be instrumented. + * For nested calls, the same Hook instance is re-used. + * For outer calls, a new Hook instance is created. + */ + boolean skipNestedCalls() default true; +} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java new file mode 100644 index 0000000..12033c4 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java @@ -0,0 +1,18 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A parameter annotated with @Returned can be used in a Hook's @After method to capture the return value of the instrumented method. + * + * Example: In order to instrument the following method: + * The parameter annotated with @Returned is optional, if the hook does not use the return value, the parameter can be omitted. + * If the instrumented method terminates exceptionally, the type's default value is assigned to the parameter, + * i.e. {@code 0} for numeric types and {@code null} for reference types. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Returned {} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java new file mode 100644 index 0000000..c82ccd5 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java @@ -0,0 +1,33 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A parameter annotated with @Thrown can be used in a Hook's @After method to capture an Exception thrown in an instrumented method. + * + * Example: In order to instrument the following method: + * + *

+ *     int div(int a, int b) {
+ *         return a / b;
+ *     }
+ * 
+ * + * A Hook could use an @After method like this: + * + *
+ *    {@literal @}After(method = "div")
+ *     void after(int a, int b, @Returned int result, @Thrown Throwable exception) {...}
+ * 
+ * + * In case everything goes well, the {@code result} will be the return value of {@code div()}, and {@code excption} will be {@code null}. + * If {@code b} is {@code 0}, the {@code result} will be {@code 0}, and {@code exception} will be an {@link ArithmeticException} (division by zero). + *

+ * The parameter annotated with @Thrown is optional, if the hook does not use the exception, the parameter can be omitted. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Thrown {} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java new file mode 100644 index 0000000..42655b8 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java @@ -0,0 +1,38 @@ +package io.agent.hookcontext; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; + +import java.util.function.BiFunction; + +/** + * See {@link #MetricDef(String, BiFunction)} + */ +public class MetricDef { + + private final String metricName; + private final BiFunction producer; + + /** + * See {@link MetricsStore}. + * @param metricName Name of the Prometheus metric, like "http_requests_total". + * @param producer Function to create a new metric and register it with the registry. + * This function will only be called once, subsequent calls to {@link MetricsStore#createOrGet(MetricDef)} + * will return the previously created metric with the specified name. + * The two parameters are the metricName as specified above, + * and the Prometheus registry where the new metric should be registered. + * For an example see JavaDoc for {@link MetricsStore}. + */ + public MetricDef(String metricName, BiFunction producer) { + this.metricName = metricName; + this.producer = producer; + } + + String getMetricName() { + return metricName; + } + + BiFunction getProducer() { + return producer; + } +} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java new file mode 100644 index 0000000..f14f085 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java @@ -0,0 +1,38 @@ +package io.agent.hookcontext; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; + +/** + * Instead of creating Prometheus Metrics directly, Hooks should use the {@link MetricsStore} like this: + *

+ * Counter httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>(
+ *         "http_requests_total",
+ *         (name, registry) -> Counter.build()
+ *                 .name(name)
+ *                 .labelNames("method", "path", "status")
+ *                 .help("Total number of http requests.")
+ *                 .register(registry)
+ * ));
+ * 
+ * The Promgent framework will take care that each metric is created only once and re-used across re-deployments in an application server. + */ +public class MetricsStore { + + private final CollectorRegistry registry; + private final ConcurrentMap metrics = new ConcurrentHashMap<>(); + + public MetricsStore(CollectorRegistry registry) { + this.registry = registry; + } + + // See {@link MetricsStore} and {@link MetricDef#MetricDef(String, BiFunction)}. + @SuppressWarnings("unchecked") + public T createOrGet(MetricDef metricDef) { + return (T) metrics.computeIfAbsent(metricDef.getMetricName(), s -> metricDef.getProducer().apply(metricDef.getMetricName(), registry)); + } +} diff --git a/war&&jar_agent/agent-framework/agent-exporter/pom.xml b/war&&jar_agent/agent-framework/agent-exporter/pom.xml new file mode 100644 index 0000000..dd57d88 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-exporter/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-exporter + agent.war + + + false + + + war + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + agent + + diff --git a/war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java b/war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java new file mode 100644 index 0000000..b8d2ac7 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java @@ -0,0 +1,29 @@ +package io.agent.exporter; + +import javax.management.ObjectName; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.management.ManagementFactory; + +/** + * This servlet simply calls the ExporterMBean via JMX and provides the result. + */ +@WebServlet("/") +public class agentExporterServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + String result = (String) ManagementFactory.getPlatformMBeanServer() + .getAttribute(new ObjectName("io.agent:type=exporter"), "TextFormat"); + response.getWriter().println(result); + } catch (Exception e) { + response.setStatus(500); + response.getWriter().println("Failed to load Exporter MBean. Are you sure the agent is running?"); + e.printStackTrace(); + } + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/pom.xml b/war&&jar_agent/agent-framework/agent-internal/pom.xml new file mode 100644 index 0000000..25e1e4f --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-internal + agent-internal classes shared across deployments + + jar + + + + + ${project.groupId} + agent-agent + ${project.version} + provided + + + + ${project.groupId} + agent-api + ${project.version} + provided + + + + net.bytebuddy + byte-buddy + 1.9.4 + + + + commons-io + commons-io + 2.6 + + + + io.prometheus + simpleclient_common + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mockito + mockito-core + 2.23.0 + test + + + + javax.servlet + javax.servlet-api + 4.0.1 + test + + + + diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java new file mode 100644 index 0000000..d3bb002 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java @@ -0,0 +1,57 @@ + + +package io.agent.internal; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.InetSocketAddress; +import java.util.Collections; + +/** + * Use the Java runtime's built-in {@link HttpServer} to export Prometheus metrics. + */ +class BuiltInServer { + + static void run(String host, String portString, CollectorRegistry registry) throws Exception { + try { + int port = Integer.parseInt(portString); + InetSocketAddress address = host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port); + HttpServer httpServer = HttpServer.create(address, 10); + httpServer.createContext("/", httpExchange -> { + if ("/metrics".equals(httpExchange.getRequestURI().getPath())) { + respondMetrics(registry, httpExchange); + } else { + respondRedirect(httpExchange); + } + }); + httpServer.start(); + } catch (NumberFormatException e) { + throw new RuntimeException("Failed to parse command line arguments: '" + portString + "' is not a valid port number."); + } + } + + private static void respondMetrics(CollectorRegistry registry, HttpExchange httpExchange) throws IOException { + StringWriter respBodyWriter = new StringWriter(); + respBodyWriter.write("# Metrics will become visible when they are updated for the first time.\n"); + TextFormat.write004(respBodyWriter, registry.metricFamilySamples()); + byte[] respBody = respBodyWriter.toString().getBytes("UTF-8"); + httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); + httpExchange.sendResponseHeaders(200, respBody.length); + httpExchange.getResponseBody().write(respBody); + httpExchange.getResponseBody().close(); + } + + private static void respondRedirect(HttpExchange httpExchange) throws IOException { + byte[] respBody = "Metrics are provided on the /metrics endpoint.".getBytes("UTF-8"); + httpExchange.getResponseHeaders().add("Location", "/metrics"); + httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); + httpExchange.sendResponseHeaders(302, respBody.length); + httpExchange.getResponseBody().write(respBody); + httpExchange.getResponseBody().close(); + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java new file mode 100644 index 0000000..de5dd09 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java @@ -0,0 +1,264 @@ + + +package io.agent.internal; + +import io.agent.agent.ClassLoaderCache; +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Returned; +import io.agent.annotations.Thrown; +import io.agent.hookcontext.MetricsStore; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Delegator is called from the Byte Buddy Advice, and calls the Hook's @Before and @After methods. + *

+ * TODO: This is called often, should be performance optimized, e.g. caching hook method handles, etc. + */ +public class Delegator { + + private static Delegator instance; // not thread-safe, but it is set only once in the agent's premain method. + + private final SortedSet hookMetadata; + private final MetricsStore metricsStore; + private final ClassLoaderCache classLoaderCache; + private final ThreadLocal, Object>> threadLocal; + + private Delegator(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { + this.hookMetadata = hookMetadata; + this.metricsStore = metricsStore; + this.classLoaderCache = classLoaderCache; + this.threadLocal = ThreadLocal.withInitial(HashMap::new); + } + + public static void init(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { + instance = new Delegator(hookMetadata, metricsStore, classLoaderCache); + } + + /** + * Should be called from the Advice's @OnMethodEnter method. Returns the list of Hooks to be passed on to after() + */ + public static List before(Class interceptedClass, Method interceptedMethod, Object[] args) { + return instance.doBefore(interceptedClass, interceptedMethod, args); + } + + private List doBefore(Class interceptedClass, Method interceptedMethod, Object[] args) { + List hookInstances = loadFromThreadLocalOrCreate(interceptedClass, interceptedMethod); + for (HookInstance hookInstance : hookInstances) { + invokeBefore(hookInstance.getInstance(), interceptedMethod, args); + } + return hookInstances; + } + + /** + * Should be called from the Advice's @OnMethodExit method. First parameter is the list of hooks returned by before() + */ + public static void after(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { + instance.doAfter(hookInstances, interceptedMethod, args, returned, thrown); + } + + private void doAfter(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { + if (hookInstances != null) { + for (HookInstance hookInstance : hookInstances) { + invokeAfter(hookInstance.getInstance(), interceptedMethod, args, returned, thrown); + if (!hookInstance.isRecursiveCall()) { + threadLocal.get().remove(hookInstance.getInstance().getClass()); + } + } + } + } + + private List loadFromThreadLocalOrCreate(Class interceptedClass, Method interceptedMethod) { + return hookMetadata.stream() + .filter(hook -> classOrInterfaceMatches(interceptedClass, hook)) + .filter(hook -> methodNameAndNumArgsMatch(interceptedMethod, hook)) + .map(hook -> loadHookClass(hook)) + .filter(hookClass -> argumentTypesMatch(hookClass, interceptedMethod)) + .filter(hookClass -> ! shouldBeSkipped(hookClass)) + .map(hookClass -> loadFromTheadLocalOrCreate(hookClass)) + .collect(Collectors.toList()); + } + + private static boolean classOrInterfaceMatches(Class classToBeInstrumented, HookMetadata hook) { + Set classesAndInterfaces = getAllSuperClassesAndInterfaces(classToBeInstrumented); + return hook.getInstruments().stream().anyMatch(classesAndInterfaces::contains); + } + + private static Set getAllSuperClassesAndInterfaces(Class clazz) { + Set result = new HashSet<>(); + addAllSuperClassesAndInterfaces(clazz, result); + return result; + } + + private static void addAllSuperClassesAndInterfaces(Class clazz, Set result) { + if (clazz == null) { + return; + } + if (result.contains(clazz.getName())) { + return; + } + result.add(clazz.getName()); + for (Class ifc : clazz.getInterfaces()) { + addAllSuperClassesAndInterfaces(ifc, result); + } + addAllSuperClassesAndInterfaces(clazz.getSuperclass(), result); + } + + private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata hook) { + return hook.getMethods().stream() + .anyMatch(instrumentedMethod -> methodNameAndNumArgsMatch(interceptedMethod, instrumentedMethod)); + } + + private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata.MethodSignature instrumentedMethod) { + if (!interceptedMethod.getName().equals(instrumentedMethod.getMethodName())) { + return false; + } + if (interceptedMethod.getParameterCount() != instrumentedMethod.getParameterTypes().size()) { + return false; + } + return true; + } + + private Class loadHookClass(HookMetadata hook) { + try { + return classLoaderCache.currentClassLoader().loadClass(hook.getHookClassName()); + } catch (ClassNotFoundException e) { + throw new HookException("Failed to load Hook class " + hook.getHookClassName() + ": " + e.getMessage(), e); + } + } + + private static boolean argumentTypesMatch(Class hookClass, Method interceptedMethod) { + List before = findHookMethods(Before.class, hookClass, interceptedMethod); + List after = findHookMethods(After.class, hookClass, interceptedMethod); + return ! (before.isEmpty() && after.isEmpty()); + } + + private static List findHookMethods(Class annotation, Class hookClass, Method interceptedMethod) throws HookException { + return Stream.of(hookClass.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(annotation)) + .filter(method -> getMethodNames(method.getAnnotation(annotation)).contains(interceptedMethod.getName())) + .filter(method -> parameterTypesMatch(method, interceptedMethod)) + .collect(Collectors.toList()); + } + + private static List getMethodNames(Annotation annotation) throws HookException { + if (Before.class.isAssignableFrom(annotation.getClass())) { + return Arrays.asList(((Before) annotation).method()); + } else if (After.class.isAssignableFrom(annotation.getClass())) { + return Arrays.asList(((After) annotation).method()); + } else { + throw new HookException("Unsupported Annotation: @" + annotation.getClass().getSimpleName() + "."); + } + } + + // TODO: We could extend this to find the "closest" match, like in Java method calls. + private static boolean parameterTypesMatch(Method hookMethod, Method interceptedMethod) { + List> hookParameterTypes = stripReturnedAndThrown(hookMethod); + if (hookParameterTypes.size() != interceptedMethod.getParameterCount()) { + return false; + } + for (int i = 0; i < hookParameterTypes.size(); i++) { + Class hookParam = hookParameterTypes.get(i); + Class interceptedParam = interceptedMethod.getParameterTypes()[i]; + if (!hookParam.equals(interceptedParam)) { + return false; + } + } + return true; + } + + private static List> stripReturnedAndThrown(Method hookMethod) { + Class[] allTypes = hookMethod.getParameterTypes(); + Annotation[][] annotations = hookMethod.getParameterAnnotations(); + if (allTypes.length != annotations.length) { + throw new HookException("Method.getParameterAnnotations() returned an unexpected value. This is a bug in agent."); + } + List> result = new ArrayList<>(); + for (int i=0; i Returned.class.equals(a) || Thrown.class.equals(a))) { + result.add(allTypes[i]); + } + } + return result; + } + + private boolean shouldBeSkipped(Class hookClass) { + return hookClass.getAnnotation(io.agent.annotations.Hook.class).skipNestedCalls() + && threadLocal.get().containsKey(hookClass); + } + + private HookInstance loadFromTheadLocalOrCreate(Class hookClass) { + Object existingHookInstance = threadLocal.get().get(hookClass); + if (existingHookInstance != null) { + return new HookInstance(existingHookInstance, true); + } else { + String errMsg = "Failed to create new instance of hook " + hookClass.getSimpleName() + ": "; + try { + Object newHookInstance = hookClass.getConstructor(MetricsStore.class).newInstance(metricsStore); + threadLocal.get().put(hookClass, newHookInstance); + return new HookInstance(newHookInstance, false); + } catch (NoSuchMethodException e) { + throw new HookException(errMsg + "Hook classes must have a public constructor with a single parameter of type " + MetricsStore.class.getSimpleName(), e); + } catch (Exception e) { + throw new HookException(errMsg + e.getMessage(), e); + } + } + } + + /** + * Invoke the matching Hook methods annotated with @Before + */ + private static void invokeBefore(Object hookInstance, Method interceptedMethod, Object[] args) throws HookException { + invoke(Before.class, hookInstance, interceptedMethod, args, null, null); + } + + /** + * Invoke the matching Hook methods annotated with @After + */ + private static void invokeAfter(Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { + invoke(After.class, hookInstance, interceptedMethod, args, returned, thrown); + } + + private static void invoke(Class annotation, Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { + if (args.length != interceptedMethod.getParameterCount()) { + throw new IllegalArgumentException("Number of provided arguments is " + args.length + ", but interceptedMethod expects " + interceptedMethod.getParameterCount() + " argument(s)."); + } + for (Method method : findHookMethods(annotation, hookInstance.getClass(), interceptedMethod)) { + try { + if (!method.isAccessible()) { + method.setAccessible(true); + } + method.invoke(hookInstance, addReturnedAndThrownArgs(method, args, returned, thrown)); + } catch (Exception e) { + throw new HookException("Failed to call " + method.getName() + "() on " + hookInstance.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + } + + private static Object[] addReturnedAndThrownArgs(Method hookMethod, Object[] args, Object returned, Throwable thrown) { + Annotation[][] annotations = hookMethod.getParameterAnnotations(); + List result = new ArrayList<>(); + int arg = 0; + for (Annotation[] annotation : annotations) { + if (Arrays.stream(annotation) + .map(Annotation::annotationType) + .anyMatch(Returned.class::equals)) { + result.add(returned); + } else if (Arrays.stream(annotation) + .map(Annotation::annotationType) + .anyMatch(Thrown.class::equals)) { + result.add(thrown); + } else { + result.add(args[arg++]); + } + } + return result.toArray(); + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java new file mode 100644 index 0000000..dfaf80c --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java @@ -0,0 +1,12 @@ +package io.agent.internal; + +public class HookException extends RuntimeException { + + public HookException(String message) { + super(message); + } + + public HookException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java new file mode 100644 index 0000000..73c2f20 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java @@ -0,0 +1,20 @@ +package io.agent.internal; + +public class HookInstance { + + private final Object instance; + private final boolean isRecursiveCall; // true if we have taken an existing instance from the ThreadLocal + + public HookInstance(Object instance, boolean isRecursiveCall) { + this.instance = instance; + this.isRecursiveCall = isRecursiveCall; + } + + public Object getInstance() { + return instance; + } + + public boolean isRecursiveCall() { + return isRecursiveCall; + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java new file mode 100644 index 0000000..21652d5 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java @@ -0,0 +1,128 @@ + +package io.agent.internal; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Metadata for a Hook class as parsed from the @Hook, @Before, and @After annotations. + */ +public class HookMetadata implements Comparable { + + private final String hookClassName; + private final SortedSet instruments; + private final SortedSet methods; + + public static class MethodSignature implements Comparable { + + private final String methodName; + private final List parameterTypes; + + public MethodSignature(String methodName, List parameterTypes) { + this.methodName = methodName; + this.parameterTypes = Collections.unmodifiableList(new ArrayList<>(parameterTypes)); + } + + public String getMethodName() { + return methodName; + } + + public List getParameterTypes() { + return parameterTypes; + } + + @Override + public String toString() { + return methodName + "(" + String.join(", ", parameterTypes) + ")"; + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && compareTo((MethodSignature) o) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(toString()); + } + + @Override + public int compareTo(MethodSignature o) { + return Comparator + .comparing(MethodSignature::getMethodName) + .thenComparing(MethodSignature::getParameterTypes, new LexicographicalComparator<>()) + .compare(this, o); + } + } + + public HookMetadata(String hookClassName, Collection instruments, Collection methods) { + this.hookClassName = hookClassName; + this.instruments = Collections.unmodifiableSortedSet(new TreeSet<>(instruments)); + this.methods = Collections.unmodifiableSortedSet(new TreeSet<>(methods)); + } + + public String getHookClassName() { + return hookClassName; + } + + public SortedSet getInstruments() { + return instruments; + } + + public SortedSet getMethods() { + return methods; + } + + @Override + public String toString() { // TODO: instruments is a Set + String delimiter = System.lineSeparator() + " * "; + return hookClassName + " instruments [" + String.join(", ", instruments) + "]:" + delimiter + String.join(delimiter, strings(methods)); + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && compareTo((HookMetadata) o) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(toString()); + } + + @Override + public int compareTo(HookMetadata o) { + return Comparator + .comparing(HookMetadata::getHookClassName) + .thenComparing(HookMetadata::getInstruments, new LexicographicalComparator<>()) + .thenComparing(HookMetadata::getMethods, new LexicographicalComparator<>()) + .compare(this, o); + } + + private List strings(Collection list) { + return list.stream().map(Object::toString).collect(Collectors.toList()); + } + + /** + * Compare SortedSet instances or List instances in lexicographical order. + */ + static class LexicographicalComparator, T extends Iterable> implements Comparator { + @Override + public int compare(T list1, T list2) { + Iterator iterator1 = list1.iterator(); + Iterator iterator2 = list2.iterator(); + while (iterator1.hasNext() && iterator2.hasNext()) { + int result = iterator1.next().compareTo(iterator2.next()); + if (result != 0) { + return result; + } + } + if (iterator1.hasNext()) { + return 1; + } + if (iterator2.hasNext()) { + return -1; + } + return 0; + } + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java new file mode 100644 index 0000000..2723b10 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java @@ -0,0 +1,321 @@ +package io.agent.internal; + +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Returned; +import io.agent.annotations.Thrown; +import io.agent.internal.HookMetadata.MethodSignature; +import net.bytebuddy.jar.asm.*; +import org.apache.commons.io.IOUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Inspect all hook classes in a JAR file and load the metadata from the @Hook, @Before, and @After annotations. + *

+ * The tricky part is that we cannot create an instance of the hook classes, because they might reference + * classes that are not available in the agent's premain phase. We need to parse the metadata without instantiating + * the hook classes. + */ +public class HookMetadataParser { + + private final SortedSet hookJars; + + public HookMetadataParser(Collection hookJars) { + this.hookJars = Collections.unmodifiableSortedSet(new TreeSet<>(hookJars)); + } + + /** + * Get the {@link HookMetadata} for the hook classes without loading the hook classes. + *

+ * We unsuccessfully tried two alternative implementations before we ended up with the current one. + * The first attempt was to use reflection like this: + *

+     *     Class hookClass = HookConfig.class.getClassLoader().loadClass(hookClassName);
+     *     for (Method m : hookClass.getMethods()) {
+     *         // ... get methodConfig
+     *     }
+     * 
+ * However, this throws a ClassNotFoundException in hookClass.getMethods(), because + * a hook method may have parameters like ServletRequest that are not available during agent startup. + *

+ * The second attempt was to analyze the hook using Byte buddy like this: + *

+     *     TypeDescription typeDesc = typePool.describe(hookClassName).resolve();
+     *     for (MethodDescription methodDesc : typeDesc.getDeclaredMethods()) {
+     *         TypeList params = methodDesc.getParameters().asTypeList().asErasures();
+     *         // ... get methodConfig
+     *     }
+     * 
+ * However, this throws a ClassNotFoundException in methodDesc.getParameters().asTypeList(). + * We did not find any way in the Byte buddy API to access a method's parameter types without loading these types. + *

+ * The current implementation uses ASM directly to analyze the hook class's byte code. + */ + SortedSet parse() throws IOException, ClassNotFoundException { + return parse(className -> true); // Parse all classes found in the JAR files. + } + + /** + * See {@link #parse()}. + * + * The classNameFilter is used to parse only specific classes from the JAR files. + */ + public SortedSet parse(Predicate classNameFilter) throws IOException, ClassNotFoundException { + SortedSet result = new TreeSet<>(); + for (String className : listAllJavaClasses(hookJars, classNameFilter)) { + byte[] binaryRepresentation = readBinaryRepresentation(className); + ClassReader classReader = new ClassReader(binaryRepresentation); + HookMetadataBuilder hookMetadata = new HookMetadataBuilder(className); + classReader.accept(new ClassVisitor(Opcodes.ASM5) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && typeEquals(desc, io.agent.annotations.Hook.class)) { + return new AnnotationValueCollector("instruments", hookMetadata::addInstruments, Opcodes.ASM5, super.visitAnnotation(desc, visible)); + } else { + return super.visitAnnotation(desc, visible); + } + } + + @Override + public MethodVisitor visitMethod(int i, String method, String desc, String signature, String[] strings) { + List parameterTypes = Arrays.stream(Type.getArgumentTypes(desc)) + .map(Type::getClassName) + .collect(Collectors.toList()); + MethodSignatureBuilder builder = hookMetadata.newMethodSignature(parameterTypes); + return new MethodVisitor(Opcodes.ASM5, super.visitMethod(i, method, desc, signature, strings)) { + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { + if (visible && typeEquals(desc, Returned.class, Thrown.class)) { + builder.markReturnedOrThrown(parameter); + } + return super.visitParameterAnnotation(parameter, desc, visible); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && typeEquals(desc, Before.class, After.class)) { + return new AnnotationValueCollector("method", builder::addMethodName, Opcodes.ASM5, super.visitAnnotation(desc, visible)); + } else { + return super.visitAnnotation(desc, visible); + } + } + }; + } + + }, ClassReader.SKIP_CODE); + if (hookMetadata.isComplete()) { + result.add(hookMetadata.build()); + } + } + return result; + } + + private byte[] readBinaryRepresentation(String className) throws ClassNotFoundException { + String classFileName = "/" + className.replace(".", "/") + ".class"; + try(InputStream stream = getResourceAsStream(classFileName)) { + if (stream == null) { + throw new ClassNotFoundException(className); + } + return IOUtils.toByteArray(stream); + } catch (IOException e) { + throw new ClassNotFoundException(className, e); + } + } + + private InputStream getResourceAsStream(String name) throws IOException { + for (Path hookJar : hookJars) { + try { + URL url; + // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. + if (Files.isDirectory(hookJar)) { + url = hookJar.toUri().resolve("." + name).toURL(); + } else if (Files.isRegularFile(hookJar)) { + url = new URL("jar:" + hookJar.toUri().toURL().toString() + "!" + name); + } else { + throw new IOException("Invalid JAR file or classes directory: " + hookJar); + } + return url.openStream(); + } catch (FileNotFoundException e) {} + } + throw new FileNotFoundException(name + " not found in [" + hookJars.stream().map(Path::toString).reduce("", (s1, s2) -> s1 + ", " + s2) + "]"); + } + + private boolean typeEquals(String typeDescriptor, Class... classes) { + for (Class clazz : classes) { + if (clazz.getName().equals(Type.getType(typeDescriptor).getClassName())) { + return true; + } + } + return false; + } + + + /** + * Helper to create {@link HookMetadata} isntances. + */ + private static class HookMetadataBuilder { + + private String hookClassName; + private List instruments = new ArrayList<>(); + private List methods = new ArrayList<>(); + + private HookMetadataBuilder(String hookClassName) { + this.hookClassName = hookClassName; + } + + private void addInstruments(String instruments) { + this.instruments.add(instruments); + } + + private MethodSignatureBuilder newMethodSignature(List parameterTypes) { + MethodSignatureBuilder builder = new MethodSignatureBuilder(); + for (String parameterType : parameterTypes) { + builder.addParameterType(parameterType); + } + methods.add(builder); + return builder; + } + + private HookMetadata build() { + SortedSet methodSignatures = new TreeSet<>(); + for (MethodSignatureBuilder builder : methods) { + methodSignatures.addAll(builder.build()); + } + return new HookMetadata(hookClassName, instruments, methodSignatures); + } + + private boolean isComplete() { + return hookClassName != null && instruments.size() > 0 && methods.size() > 0; + } + } + + /** + * Helper to create {@link MethodSignature} instances. + *

+ * There's a slight mismatch between a {@link MethodSignatureBuilder} and a {@link MethodSignature}: + * While a {@link MethodSignature} can only contain a single method name, + * the {@link MethodSignatureBuilder} may have multiple method names. + * Therefore, the {@link #build()} method might create multiple {@link MethodSignature} instances + * out of a single {@link MethodSignatureBuilder}. + */ + private static class MethodSignatureBuilder { + + private static class ParameterType { + final String type; + boolean isReturnedOrThrown; // method parameters annotated with @Returned or @Thrown will be ignored. + + private ParameterType(String type) { + this.type = type; + this.isReturnedOrThrown = false; + } + } + + SortedSet methodNames = new TreeSet<>(); + List parameterTypes = new ArrayList<>(); + + private void addMethodName(String methodName) { + methodNames.add(methodName); + } + + private void addParameterType(String parameterType) { + parameterTypes.add(new ParameterType(parameterType)); + } + + private SortedSet build() { + List strippedParameterTypes = parameterTypes.stream() + .filter(p -> ! p.isReturnedOrThrown) + .map(p -> p.type) + .collect(Collectors.toList()); + SortedSet result = new TreeSet<>(); + for (String methodName : methodNames) { + result.add(new MethodSignature(methodName, strippedParameterTypes)); + } + return result; + } + + public void markReturnedOrThrown(int parameter) { + // We know that parameter is a valid index in parameterTypes. + parameterTypes.get(parameter).isReturnedOrThrown = true; + } + } + + /** + * Collect annotation values from @Hook(instruments = ...) or @Before(method = ...) or @After(method = ...) + *

+ * Example: If the annotation is @Before(method={"service", "doFilter"}) + * then using a AnnotationValueCollector with methodName=="method" will add the call + * consumer.accept("service") and consumer.accept("doFilter") + */ + private static class AnnotationValueCollector extends AnnotationVisitor { + private final Consumer consumer; + private final String methodName; + private AnnotationValueCollector(String methodName, Consumer consumer, int api, AnnotationVisitor av) { + super(api, av); + this.consumer = consumer; + this.methodName = methodName; + } + @Override + public AnnotationVisitor visitArray(String name) { + if (methodName.equals(name)) { + return new AnnotationVisitor(Opcodes.ASM5, super.visitArray(name)) { + @Override + public void visit(String name, Object value) { + consumer.accept(value.toString()); + } + }; + } else { + return super.visitArray(name); + } + } + } + + /** + * List all Java classes found in the JAR files. + * + */ + private static Set listAllJavaClasses(Set hookJars, Predicate classNameFilter) throws IOException { + Set result = new TreeSet<>(); + for (Path hookJar : hookJars) { + // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. + if (hookJar.toFile().isDirectory()) { + try (Stream dirEntries = Files.walk(hookJar)) { + addClassNames(dirEntries.map(hookJar::relativize).map(Path::toString), result, classNameFilter); + } + } + else if (hookJar.toFile().isFile()) { + try (ZipFile zipFile = new ZipFile(hookJar.toFile())) { + addClassNames(zipFile.stream().map(ZipEntry::getName), result, classNameFilter); + } + } else { + throw new IOException(hookJar + ": Failed to read file or directory."); + } + } + return result; + } + + /** + * Convert class file paths to class names and add them to result. + */ + private static void addClassNames(Stream paths, Collection result, Predicate classNameFilter) { + paths + .filter(name -> name.endsWith(".class")) + .map(name -> name.substring(0, name.length() - ".class".length())) + .map(name -> name.startsWith("/") ? name.substring(1) : name) + .map(name -> name.replace("/", ".")) + .filter(classNameFilter) + .collect(Collectors.toCollection(() -> result)); + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java new file mode 100644 index 0000000..4e9ac04 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java @@ -0,0 +1,141 @@ +package io.agent.internal; + +import static net.bytebuddy.matcher.ElementMatchers.isAbstract; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import javax.management.ObjectName; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import io.agent.agent.ClassLoaderCache; +import io.agent.hookcontext.MetricsStore; +import io.agent.internal.HookMetadata.MethodSignature; +import io.agent.internal.jmx.Exporter; +import io.agent.internal.jmx.agentCollectorRegistry; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class agent { + + public static void premain(String agentArgs, Instrumentation inst) { + try { + agentCollectorRegistry registry = new agentCollectorRegistry(); + ManagementFactory.getPlatformMBeanServer().registerMBean(new Exporter(registry), new ObjectName("io.agent:type=exporter")); + Map args = parseCmdline(agentArgs); + if (args.containsKey("port")) { + BuiltInServer.run(args.get("host"), args.get("port"), registry); + } + ClassLoaderCache classLoaderCache = ClassLoaderCache.getInstance(); + List hookJars = classLoaderCache.getPerDeploymentJars(); + SortedSet hookMetadata = new HookMetadataParser(hookJars).parse(); + MetricsStore metricsStore = new MetricsStore(registry); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + printHookMetadata(hookMetadata); + + AgentBuilder agentBuilder = new AgentBuilder.Default(); + agentBuilder = applyHooks(agentBuilder, hookMetadata, classLoaderCache); + agentBuilder + .disableClassFormatChanges() + // .with(AgentBuilder.Listener.StreamWriting.toSystemError()) // use this to see exceptions thrown in instrumented code + .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) + .with(AgentBuilder.TypeStrategy.Default.REDEFINE) + .installOn(inst); + + // TODO -- the following is an experiment supporting collectors directly (in addition to hooks) + // io.prometheus.client.Collector jmxCollector = (io.prometheus.client.Collector) classLoaderCache.currentClassLoader().loadClass("io.agent.collectors.JmxCollector").newInstance(); + // registry.registerNoJmx(jmxCollector); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * Add {@link ElementMatcher} for the hooks. + */ + private static AgentBuilder applyHooks(AgentBuilder agentBuilder, SortedSet hookMetadata, ClassLoaderCache classLoaderCache) { + Map> instruments = getInstruments(hookMetadata); + for (Map.Entry> entry : instruments.entrySet()) { + String instrumentedClassName = entry.getKey(); + Set instrumentedMethods = entry.getValue(); + agentBuilder = agentBuilder + .type(ElementMatchers.hasSuperType(named(instrumentedClassName))) + .transform(new AgentBuilder.Transformer.ForAdvice() + .include(classLoaderCache.currentClassLoader()) // must be able to load agentAdvice + .advice(matchAnyMethodIn(instrumentedMethods), agentAdvice.class.getName()) + ); + } + return agentBuilder; + } + + /** + * key: name of instrumented class or interface, value: set of instrumented methods for that class or interface + */ + public static Map> getInstruments(Set hooks) { + Map> result = new TreeMap<>(); + for (HookMetadata hookMetadata : hooks) { + for (String instruments : hookMetadata.getInstruments()) { + if (!result.containsKey(instruments)) { + result.put(instruments, new TreeSet<>()); + } + result.get(instruments).addAll(hookMetadata.getMethods()); + } + } + return result; + } + + /** + * Returns a byte buddy matcher matching any method contained in methodSignatures. + */ + public static ElementMatcher matchAnyMethodIn(Set methodSignatures) { + ElementMatcher.Junction methodMatcher = ElementMatchers.none(); + for (MethodSignature methodSignature : methodSignatures) { + ElementMatcher.Junction junction = ElementMatchers + .named(methodSignature.getMethodName()) + .and(not(isAbstract())) + .and(takesArguments(methodSignature.getParameterTypes().size())); + for (int i = 0; i < methodSignature.getParameterTypes().size(); i++) { + junction = junction.and(takesArgument(i, named(methodSignature.getParameterTypes().get(i)))); + } + methodMatcher = methodMatcher.or(junction); + } + return methodMatcher; + } + + /** + * Parse a comma-separated list of key/value pairs. Example: "host=localhost,port=9300" + */ + private static Map parseCmdline(String agentArgs) { + Map result = new HashMap<>(); + if (agentArgs != null) { + for (String keyValueString : agentArgs.split(",")) { + String[] keyValue = keyValueString.split("="); + if (keyValue.length != 2) { + throw new RuntimeException("Failed to parse command line arguments '" + agentArgs + "'. " + + "Expecting a comma-separated list of key/value pairs, as for example 'host=localhost,port=9300'."); + } + result.put(keyValue[0], keyValue[1]); + } + } + return result; + } + + private static void printHookMetadata(SortedSet hookMetadata) { + System.out.println("agent instrumenting the following classes or interfaces:"); + for (HookMetadata m : hookMetadata) { + System.out.println(m); + } + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java new file mode 100644 index 0000000..8566f3d --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java @@ -0,0 +1,64 @@ +package io.agent.internal; + +import io.agent.agent.ClassLoaderCache; +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +import java.lang.reflect.Method; +import java.util.List; + +import static net.bytebuddy.asm.Advice.*; + +public class agentAdvice { + + // TODO: + // Should we move this class into it's own maven module + // to make clear that it cannot reference other classes from agent-internal? + + @OnMethodEnter + @SuppressWarnings("unchecked") + public static List before( + @This(optional = true) Object that, + @Origin Method method, + @AllArguments Object[] args + ) { + // that is null when instrumenting static methods. + Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); + try { + // The following code is equivalent to: + // return Delegator.before(that, method, args); + // However, the Delegator class will not be available in the context of the instrumented method, + // so we must use our agent class loader to load the Delegator class and do the call via reflection. + Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.agent.internal.Delegator"); + Method beforeMethod = delegator.getMethod("before", Class.class, Method.class, Object[].class); + return (List) beforeMethod.invoke(null, clazz, method, args); + } catch (Exception e) { + System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); + e.printStackTrace(); + return null; + } + } + + @OnMethodExit(onThrowable = Throwable.class) + public static void after( + @Enter List hooks, + @This(optional = true) Object that, + @Origin Method method, + @AllArguments Object[] args, + @Return(typing = Assigner.Typing.DYNAMIC) Object returned, // support void == null and int == Integer + @Thrown Throwable thrown + ) { + try { + // The following code is equivalent to: + // Delegator.after(hooks, method, args); + // However, the Delegator class will not be available in the context of the instrumented method, + // so we must use our agent class loader to load the Delegator class and do the call via reflection. + Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.agent.internal.Delegator"); + Method afterMethod = delegator.getMethod("after", List.class, Method.class, Object[].class, Object.class, Throwable.class); + afterMethod.invoke(null, hooks, method, args, returned, thrown); + } catch (Exception e) { + Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); + System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); + e.printStackTrace(); + } + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java new file mode 100644 index 0000000..0a89b53 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java @@ -0,0 +1,27 @@ +package io.agent.internal.jmx; + +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import java.io.IOException; +import java.io.StringWriter; + +public class Exporter implements ExporterMBean { + + private final CollectorRegistry registry; + + public Exporter(CollectorRegistry registry) { + this.registry = registry; + } + + @Override + public String getTextFormat() { + try { + StringWriter result = new StringWriter(); + TextFormat.write004(result, registry.metricFamilySamples()); + return result.toString(); + } catch (IOException e) { + throw new RuntimeException("Unexpected error when writing metrics to a String: " + e.getMessage(), e); + } + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java new file mode 100644 index 0000000..1812b3d --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java @@ -0,0 +1,5 @@ +package io.agent.internal.jmx; + +public interface ExporterMBean { + String getTextFormat(); +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java new file mode 100644 index 0000000..30f4742 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java @@ -0,0 +1,35 @@ + + +package io.agent.internal.jmx; + +import io.prometheus.client.Collector; + +import java.util.HashMap; +import java.util.Map; + +public class Metric implements MetricMBean { + + private final Collector metric; + + Metric(Collector metric) { + this.metric = metric; + } + + /** + * @see MetricMBean#getValues() + */ + @Override + public Map, Double> getValues() { + Map, Double> result = new HashMap<>(); + for (Collector.MetricFamilySamples samples : metric.collect()) { + for (Collector.MetricFamilySamples.Sample sample : samples.samples) { + Map labels = new HashMap<>(); + for (int i = 0; i < sample.labelNames.size(); i++) { + labels.put(sample.labelNames.get(i), sample.labelValues.get(i)); + } + result.put(labels, sample.value); + } + } + return result; + } +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java new file mode 100644 index 0000000..151ca17 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java @@ -0,0 +1,16 @@ + + +package io.agent.internal.jmx; + +import java.util.Map; + +public interface MetricMBean { + + /** + * Get the values in a representation that can be used in MXBeans in JMX. + *

+ * The result is a map of labels -> value. + * The labels themselves are represented as a key -> value map. + */ + Map, Double> getValues(); +} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java new file mode 100644 index 0000000..89764cd --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java @@ -0,0 +1,52 @@ +package io.agent.internal.jmx; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.SimpleCollector; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; + +/** + * This is like the regular {@link CollectorRegistry}, except that when you {@link #register(Collector)} a metric, + * the metric will also be registered as an MBean in the JMX platform server. + */ +public class agentCollectorRegistry extends CollectorRegistry { + + @Override + public void register(Collector metric) { + super.register(metric); + try { + ManagementFactory.getPlatformMBeanServer().registerMBean(new Metric(metric), makeObjectName((SimpleCollector) metric)); + } catch (Exception e) { + throw new RuntimeException("Failed to register metric: " + e.getMessage(), e); + } + } + + private static ObjectName makeObjectName(SimpleCollector metric) throws MalformedObjectNameException { + return makeObjectName(getFullName(metric)); + } + + private static ObjectName makeObjectName(String fullname) throws MalformedObjectNameException { + return new ObjectName("io.agent:type=metrics,name=" + fullname); + } + + private static String getFullName(SimpleCollector metric) { + // Unfortunately, there is no public API to get the 'fullname' of a metric. We use reflection to get it anyway. + try { + Field field = SimpleCollector.class.getDeclaredField("fullname"); + field.setAccessible(true); + return (String) field.get(metric); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException("Failed to access " + metric.getClass().getName() + ".fullname. " + + "This is probably because the internal implementation of the client library has changed. " + + "You should adapt the agent accordingly.", e); + } + } + + public void registerNoJmx(Collector collector) { + super.register(collector); + } +} diff --git a/war&&jar_agent/agent-framework/agent-loader/pom.xml b/war&&jar_agent/agent-framework/agent-loader/pom.xml new file mode 100644 index 0000000..d4a1455 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-loader/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-loader + agent-loader attach agent to a running JVM + + jar + + + + org.junit.jupiter + junit-jupiter-api + test + + + + com.sun + tools + 1.8 + system + ${java.home}/../lib/tools.jar + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + io.agent.loader.agentLoader + + + + + + + diff --git a/war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java b/war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java new file mode 100644 index 0000000..e096f25 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java @@ -0,0 +1,87 @@ +package io.agent.loader; + +import com.sun.tools.attach.AgentLoadException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; + +public class agentLoader { + + public static void main(String[] args) throws Exception { + int pid = getIntArg(args, "-pid"); + int port = getIntArg(args, "-port"); + String agentJar = getStringArg(args, "-agent"); + agentLoader.loadagent(agentJar, pid, port); + } + + private static void loadagent(String agentJar, int pid, int port) throws Exception { + VirtualMachineDescriptor vmd = findVirtualMachine(Integer.toString(pid)); + if (vmd == null) { + System.err.println("No Java process found with PID " + pid); + System.exit(-1); + } + VirtualMachine vm = null; + try { + vm = VirtualMachine.attach(vmd); + vm.loadAgent(agentJar, "port=" + port); + } catch (AgentLoadException e) { + System.err.println("Failed to attach agent: " + getMessage(e)); + } finally { + if (vm != null) { + vm.detach(); + } + } + } + + private static VirtualMachineDescriptor findVirtualMachine(String pid) { + for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { + if (vmd.id().equalsIgnoreCase(pid)) { + return vmd; + } + } + return null; + } + + private static String getMessage(AgentLoadException e) { + switch (e.getMessage()) { + case "-4": + return "Insuffient memory"; + case "100": + return "Agent JAR not found or no Agent-Class attribute"; + case "101": + return "Unable to add JAR file to system class path"; + case "102": + return "Agent JAR loaded but agent failed to initialize"; + default: + return e.getMessage(); + } + } + + private static int getIntArg(String[] args, String option) { + String stringArg = getStringArg(args, option); + try { + return Integer.parseInt(stringArg); + } catch (NumberFormatException e) { + System.err.println(option + " " + stringArg + ": invalid argument"); + System.exit(-1); + return 0; // will never happen + } + } + + private static String getStringArg(String[] args, String option) { + for (int pos : new int[]{0, 2, 4}) { + if (args.length < pos + 2) { + printUsageAndExit(); + } + if (option.equals(args[pos])) { + return args[pos+1]; + } + } + printUsageAndExit(); + return null; // will never happen + } + + private static void printUsageAndExit() { + System.err.println("Usage: java -cp $JAVA_HOME/lib/tools.jar:/path/to/agent-loader.jar io.agent.loader.agentLoader -agent /path/to/agent.jar -port 9300 -pid "); + System.exit(-1); + } +} diff --git a/war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml b/war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml new file mode 100644 index 0000000..d626cc6 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml @@ -0,0 +1,73 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-maven-plugin + agent-maven-plugin + + maven-plugin + + + + + io.agent + agent-internal + 1.0-SNAPSHOT + runtime + + + + io.agent + agent-agent + 1.0-SNAPSHOT + runtime + + + + io.agent + agent-api + 1.0-SNAPSHOT + runtime + + + + org.apache.maven + maven-plugin-api + 3.6.0 + provided + + + + org.apache.maven + maven-core + 3.6.0 + provided + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + provided + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + agent + + + + + diff --git a/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java b/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java new file mode 100644 index 0000000..780d370 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java @@ -0,0 +1,165 @@ + + +package io.agent.plugin; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import static java.util.stream.Collectors.toList; + +/** + * Hard-coded list of JAR files that must go into the agent.jar. + * Versions are resolved through Maven. + * If the user adds a duplicate JAR with a different version, the plugin fails. + */ +class AgentDependencies { + + private final String pluginGroupId; + private final List dependencies; + + /** + * Hard-coded dependencies without versions (versions are resolved dynamically). + */ + private static class ExpectedDependency { + + final String groupId; + final String artifactId; + + private ExpectedDependency(String groupId, String artifactId) { + this.groupId = groupId; + this.artifactId = artifactId; + } + } + + private AgentDependencies(String pluginGroupId, List dependencies) { + this.pluginGroupId = pluginGroupId; + this.dependencies = dependencies; + } + + static AgentDependencies init(PluginDescriptor pluginDescriptor) throws MojoExecutionException { + + String pluginGroupId = pluginDescriptor.getGroupId(); + String pluginArtifactId = pluginDescriptor.getArtifactId(); + + List expectedDependencies = Arrays.asList( + new ExpectedDependency(pluginGroupId, "agent-agent"), + new ExpectedDependency(pluginGroupId, "agent-internal"), + new ExpectedDependency(pluginGroupId, "agent-api"), + new ExpectedDependency("io.prometheus", "simpleclient_common"), + new ExpectedDependency("io.prometheus", "simpleclient"), + new ExpectedDependency("net.bytebuddy", "byte-buddy"), + new ExpectedDependency("commons-io", "commons-io") + ); + + List actualDependencies = resolveVersions(pluginDescriptor, pluginArtifactId, expectedDependencies); + failUnlessComplete(actualDependencies, expectedDependencies, pluginArtifactId); + return new AgentDependencies(pluginGroupId, actualDependencies); + } + + /** + * Artifact for the agent-agent module. + */ + Artifact getAgentArtifact() { + return dependencies.stream() + .filter(isAgent()) + .findFirst() + .get(); // We know it's present. + } + + /** + * Artifact for all other runtime dependencies except agent-agent. + */ + List getDependencies() { + return dependencies.stream() + .filter(isAgent().negate()) + .collect(toList()); + } + + void assertNoConflict(Artifact artifact) throws MojoExecutionException { + Optional builtInVersion = dependencies.stream() + .filter(dependency -> dependency.getGroupId().equals(artifact.getGroupId())) + .filter(dependency -> dependency.getArtifactId().equals(artifact.getArtifactId())) + .map(Artifact::getVersion) + .findFirst(); + if (builtInVersion.isPresent() && ! builtInVersion.get().equals(artifact.getVersion())) { + String artifactName = artifact.getGroupId() + ":" + artifact.getArtifactId(); + throw new MojoExecutionException("Conflicting dependencies: Your project includes " + artifactName + + " version " + artifact.getVersion() + " but the agent-maven-plugin is built with version " + builtInVersion.get()); + } + } + + private static List resolveVersions(PluginDescriptor pluginDescriptor, String pluginArtifactId, List expectedDependencies) throws MojoExecutionException { + List actualDependencies = new ArrayList<>(); + for (Artifact artifact : pluginDescriptor.getArtifacts()) { + if (! isExpected(artifact, expectedDependencies)) { + continue; + } + if (isKnown(artifact, actualDependencies)) { + continue; + } + failOnVersionConflict(artifact, actualDependencies, pluginArtifactId); + actualDependencies.add(artifact); + } + return actualDependencies; + } + + private Predicate isAgent() { + return artifact -> artifact.getGroupId().equals(pluginGroupId) && artifact.getArtifactId().equals("agent-agent"); + } + + + private static Predicate expectedDependencyMatcher(Artifact artifact) { + return expectedDependency -> expectedDependency.groupId.equals(artifact.getGroupId()) && + expectedDependency.artifactId.equals(artifact.getArtifactId()); + } + + private static Predicate expectedDependencyMatcher(ExpectedDependency expectedDependency) { + return artifact -> expectedDependencyMatcher(artifact).test(expectedDependency); + } + + private static Predicate artifactMatcherWithoutVersion(Artifact artifact) { + return other -> artifact.getGroupId().equals(other.getGroupId()) && + artifact.getArtifactId().equals(other.getArtifactId()); + } + + private static Predicate artifactMatcherWithVersion(Artifact artifact) { + return other -> artifactMatcherWithoutVersion(artifact).test(other) && + artifact.getVersion().equals(other.getVersion()); + } + + private static boolean isExpected(Artifact artifact, List expectedDependencies) { + return expectedDependencies.stream().anyMatch(expectedDependencyMatcher(artifact)); + } + + private static boolean isKnown(Artifact artifact, List knownArtifacts) { + return knownArtifacts.stream().anyMatch(artifactMatcherWithVersion(artifact)); + } + + private static void failOnVersionConflict(Artifact artifact, List knownArtifacts, String pluginArtifactId) throws MojoExecutionException { + Optional conflictingVersion = knownArtifacts.stream() + .filter(artifactMatcherWithoutVersion(artifact)) + .filter(artifactMatcherWithVersion(artifact).negate()) // same version -> not conflicting + .findFirst() + .map(Artifact::getVersion); + if (conflictingVersion.isPresent()) { + String artifactName = artifact.getGroupId() + artifact.getArtifactId(); + throw new MojoExecutionException("version conflict in " + pluginArtifactId + ": " + artifactName + " found in version " + artifact.getVersion() + " and version " + conflictingVersion.get()); + } + } + + private static void failUnlessComplete(List actualDependencies, List expectedDependencies, String pluginArtifactId) throws MojoExecutionException { + for (ExpectedDependency expected : expectedDependencies) { + if (actualDependencies.stream().noneMatch(expectedDependencyMatcher(expected))) { + String dependencyName = expected.groupId + ":" + expected.artifactId; + throw new MojoExecutionException("Plugin dependency " + dependencyName + " missing. This is a bug in " + pluginArtifactId + "."); + } + } + } +} diff --git a/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java b/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java new file mode 100644 index 0000000..eb9eca0 --- /dev/null +++ b/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java @@ -0,0 +1,123 @@ + + +package io.agent.plugin; + +import org.apache.maven.plugin.MojoExecutionException; +import org.codehaus.plexus.util.IOUtil; + +import java.io.*; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +class AgentJar implements AutoCloseable { + + private final String jarName; + private final JarOutputStream jarOutputStream; + private final Set content = new HashSet<>(); + + enum Directory { + SHARED_JARS("shared-jars/"), // Directory entries in JAR file must end in '/' + PER_DEPLOYMENT_JARS("per-deployment-jars/"); + + private final String name; + + Directory(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + private AgentJar(String jarName, JarOutputStream jarOutputStream) { + this.jarName = jarName; + this.jarOutputStream = jarOutputStream; + } + + static AgentJar create(File jarFile) throws MojoExecutionException { + try { + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile))); + return new AgentJar(jarFile.getName(), jarOutputStream); + } catch (IOException e) { + throw new MojoExecutionException("Error creating " + jarFile.getName() + ": " + e.getMessage(), e); + } + } + + void addFile(File srcFile, String targetFileName, Directory targetDir) throws MojoExecutionException { + String destPath = targetDir.getName() + targetFileName; + if (content.contains(destPath)) { + return; + } + makeDirsRecursively(destPath); + content.add(destPath); + try (InputStream in = new FileInputStream(srcFile)) { + jarOutputStream.putNextEntry(new JarEntry(destPath)); + IOUtil.copy(in, jarOutputStream); + } catch (IOException e) { + throw new MojoExecutionException("Error adding " + srcFile.getName() + " to target JAR: " + e.getMessage(), e); + } + } + + void addFile(File srcFile, Directory targetDir) throws MojoExecutionException { + addFile(srcFile, srcFile.getName(), targetDir); + } + + void extractJar(File jar, ManifestTransformer manifestTransformer) throws MojoExecutionException { + try (JarFile jarFile = new JarFile(jar)) { + for (Enumeration jarEntries = jarFile.entries(); jarEntries.hasMoreElements(); ) { + JarEntry jarEntry = jarEntries.nextElement(); + if (manifestTransformer.canTransform(jarEntry)) { + jarEntry = manifestTransformer.transform(jarEntry); + } + if (!jarEntry.isDirectory() && !content.contains(jarEntry.getName())) { + content.add(jarEntry.getName()); + makeDirsRecursively(jarEntry.getName()); + try (InputStream in = getInputStream(jarEntry, jarFile, manifestTransformer)) { + jarOutputStream.putNextEntry(jarEntry); + IOUtil.copy(in, jarOutputStream); + } + } + } + } catch (IOException e) { + throw new MojoExecutionException("Error adding " + jar.getName() + " to target JAR: " + e.getMessage(), e); + } + } + + private InputStream getInputStream(JarEntry jarEntry, JarFile jarFile, ManifestTransformer manifestTransformer) throws IOException, MojoExecutionException { + InputStream in = jarFile.getInputStream(jarEntry); + if (manifestTransformer.canTransform(jarEntry)) { + in = manifestTransformer.transform(in); + } + return in; + } + + private void makeDirsRecursively(String path) throws MojoExecutionException { + String[] parts = path.split("/+"); + String segment = ""; + for (int i=0; i + 4.0.0 + + io.agent + agent-framework + 1.0-SNAPSHOT + + pom + agent framework + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 5.3.1 + + + + + + agent-loader + + agent-agent + agent-api + agent-internal + agent-exporter + agent-loader + agent-maven-plugin + + + + + default + + true + + + agent-agent + agent-api + agent-internal + agent-exporter + agent-maven-plugin + + + + + + + + + io.prometheus + simpleclient_common + 0.5.0 + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + test + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 2.22.1 + + + + + diff --git a/war&&jar_agent/war_agent/pom-with-docker-tests.xml b/war&&jar_agent/war_agent/pom-with-docker-tests.xml new file mode 100644 index 0000000..94d727f --- /dev/null +++ b/war&&jar_agent/war_agent/pom-with-docker-tests.xml @@ -0,0 +1,386 @@ + + 4.0.0 + + io.agent + agent-example + 1.0-SNAPSHOT + + agent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.agent + agent-api + ${agent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + agent + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.1 + + + + + + io.agent + agent-maven-plugin + ${agent.framework.version} + + + agent + package + + build + + + + + + + + io.fabric8 + docker-maven-plugin + 0.27.2 + + true + + + + + ${project.artifactId}/oracle-jdk-8 + + ubuntu:17.10 + /opt + + apt-get update ; apt-get upgrade -y + apt-get install -y curl git neovim sudo + curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz + tar xfz jdk-8u192-linux-x64.tar.gz + curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz + tar xfz apache-maven-3.6.0-bin.tar.gz + + echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ + echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; + + adduser --disabled-password --gecos '' agent + echo 'agent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + + agent + + /opt/jdk1.8.0_192 + /opt/apache-maven-3.6.0 + /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH + + + + + + + ${project.artifactId}/wildfly-kitchensink + + ${project.artifactId}/oracle-jdk-8 + /home/agent + + curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz + tar xfz wildfly-10.1.0.Final.tar.gz + rm wildfly-10.1.0.Final.tar.gz + git clone https://github.com/wildfly/quickstart.git + cd quickstart; git checkout 10.x + cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package + mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments + + mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments + rm -rf ./quickstart ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/wildfly-kitchensink-agent + + ${project.artifactId}/wildfly-kitchensink + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ + echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ + echo 'export JAVA_OPTS="' >> run.sh ; \ + echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ + echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ + echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent' >> run.sh ; \ + echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ + echo ' -javaagent:../agent.jar=port=9300' >> run.sh ; \ + echo '"' >> run.sh ; \ + echo >> run.sh ; \ + echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/agent + agent:agent:agent + + + + target/agent.jar + / + + + + + + + + + + ${wildfly.port}:8080 + ${agent.port}:9300 + + + WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms + + + ./run.sh + + true + [Wildfly] + + + + + + + ${project.artifactId}/spring + + ${project.artifactId}/oracle-jdk-8 + /home/agent + + git clone https://github.com/spring-guides/gs-accessing-data-rest.git + cd gs-accessing-data-rest/complete; mvn --batch-mode package + mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . + rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/spring-agent + + ${project.artifactId}/spring + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'java -javaagent:agent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/agent + agent:agent:agent + + + + target/agent.jar + / + + + + + + + + + + ${spring.port}:8080 + ${agent.port}:9300 + + + Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) + + + ./run.sh + + true + [Spring Boot] + + + + + + + + + start + pre-integration-test + + build + start + + + + stop + post-integration-test + + stop + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + none + + skip-docker-tests + + + + wildfly + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/wildfly-kitchensink, + ${project.artifactId}/wildfly-kitchensink-agent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/WildflyIT.java + + + + + http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink + http://${docker.host.address}:${agent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + spring + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/spring, + ${project.artifactId}/spring-agent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/SpringIT.java + + + + + http://${docker.host.address}:${spring.port} + http://${docker.host.address}:${agent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + diff --git a/war&&jar_agent/war_agent/pom.xml b/war&&jar_agent/war_agent/pom.xml new file mode 100644 index 0000000..3686b0b --- /dev/null +++ b/war&&jar_agent/war_agent/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + io.agent + agent-example + 1.0-SNAPSHOT + + agent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.agent + agent-api + ${agent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + agent + + + io.agent + agent-maven-plugin + ${agent.framework.version} + + + agent + package + + build + + + + + + + + diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java b/war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java new file mode 100644 index 0000000..15f12e8 --- /dev/null +++ b/war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java @@ -0,0 +1,36 @@ + + +package io.agent.collectors; + +import com.sun.management.OperatingSystemMXBean; +import io.prometheus.client.Collector; + +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) +// TODO -- This class is not loaded by default, see commented-out lines in io.agent.internal.agent.premain() +// See JmxCollector in jmx_exporter +public class JmxCollector extends Collector implements Collector.Describable { + + @Override + public List collect() { + List result = new ArrayList<>(); + result.add(collectOperatingSystemMetrics()); + return Collections.unmodifiableList(result); + } + + @Override + public List describe() { + return new ArrayList<>(); + } + + private MetricFamilySamples collectOperatingSystemMetrics() { + OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); + return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); + } +} diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java new file mode 100644 index 0000000..a0c5168 --- /dev/null +++ b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java @@ -0,0 +1,33 @@ +package io.agent.hooks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Store the HTTP context in a thread-local, so that we know which database call was triggered by which REST service. + */ +class HttpContext { + + static class Key {} + + static final Key HTTP_METHOD = new Key<>(); + static final Key HTTP_PATH = new Key<>(); + + private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); + + static void put(Key key, T value) { + threadLocal.get().put(key, value); + } + + @SuppressWarnings("unchecked") + static Optional get(Key key) { + return Optional.ofNullable((T) threadLocal.get().get(key)); + } + + static void clear(Key... keys) { + for (Key key : keys) { + threadLocal.get().remove(key); + } + } +} diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java new file mode 100644 index 0000000..05255ca --- /dev/null +++ b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java @@ -0,0 +1,129 @@ + + +package io.agent.hooks; + +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Hook; +import io.agent.hookcontext.MetricDef; +import io.agent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import java.util.concurrent.TimeUnit; + +import static io.agent.hooks.HttpContext.HTTP_METHOD; +import static io.agent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "java.sql.Statement", + "java.sql.Connection" +}) +public class JdbcHook { + + private final Counter sqlQueriesTotal; + private final Summary sqlQueriesDuration; + private long startTime = 0; + + public JdbcHook(MetricsStore metricsStore) { + + sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( + "sql_queries_total", + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "query") + .help("Total number of sql queries.") + .register(registry) + )); + + sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( + "sql_query_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "query") + .help("Duration for serving the sql queries in seconds.") + .register(registry) + )); + } + + private String stripValues(String query) { + // We want the structure of the query as labels, not the actual values. + // Therefore, we replace: + // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') + // with + // insert into Member (id, name, email, phone_number) values (...) + return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); + } + + // --- before + + @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void before(String sql) { + startTime = System.nanoTime(); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int autoGeneratedKeys) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int[] columnIndexes) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, String[] columnNames) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + before(sql); + } + + // --- after + + @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void after(String sql) throws Exception { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); + String path = HttpContext.get(HTTP_PATH).orElse("no http context"); + String query = stripValues(sql); + sqlQueriesTotal.labels(method, path, query).inc(); + sqlQueriesDuration.labels(method, path, query).observe(duration); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int autoGeneratedKeys) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int[] columnIndexes) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, String[] columnNames) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { + after(sql); + } +} diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java new file mode 100644 index 0000000..70bcf4d --- /dev/null +++ b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java @@ -0,0 +1,108 @@ + + +package io.agent.hooks; + +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Hook; +import io.agent.hookcontext.MetricDef; +import io.agent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.concurrent.TimeUnit; + +import static io.agent.hooks.HttpContext.HTTP_METHOD; +import static io.agent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "javax.servlet.Servlet", + "javax.servlet.Filter" +}) +public class ServletHook { + + private final Counter httpRequestsTotal; + private final Summary httpRequestsDuration; + private long startTime = 0; + + public ServletHook(MetricsStore metricsStore) { + + httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( + "http_requests_total", + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "status") + .help("Total number of http requests.") + .register(registry) + )); + + httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( + "http_request_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "status") + .help("Duration for serving the http requests in seconds.") + .register(registry) + )); + } + + private String stripPathParameters(String path) { + + // The URL path may include path parameters. + // For example, REST URLs for querying an item might look like this: + // + // /item/1 + // /item/2 + // /item/3 + // etc. + // + // We don't want to create a new Prometheus label for each of these paths. + // Rather, we want a single label like this: + // + // /item/{id} + // + // This method replaces path parameters with placeholders. It is application specific and + // should be adapted depending on the actual paths in an application. + // For the demo, we just replace all numbers with {id}. + + return path + .replaceAll("/[0-9]+", "/{id}") + .replaceAll("/;jsessionid=\\w*", "") + .replaceAll("/$", "") + .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." + } + + @Before(method = {"service", "doFilter"}) + public void before(ServletRequest request, ServletResponse response) { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletRequest req = (HttpServletRequest) request; + HttpContext.put(HTTP_METHOD, req.getMethod()); + HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); + startTime = System.nanoTime(); + } + } + + // Return Werte und Exceptions als Parameter + @After(method = {"service", "doFilter"}) + public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletResponse resp = (HttpServletResponse) response; + try { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).get(); + String path = HttpContext.get(HTTP_PATH).get(); + httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); + httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); + } finally { + HttpContext.clear(HTTP_METHOD, HTTP_PATH); + } + } + } +} diff --git a/war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java b/war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java new file mode 100644 index 0000000..d40844b --- /dev/null +++ b/war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java @@ -0,0 +1,80 @@ +// Copyright 2017 The agent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.agent.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.junit.jupiter.api.Test; + +public class SpringIT { + + /** + * Run some HTTP queries against a Docker container from image agent/spring-agent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. + */ + @Test + public void testSpring() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request metricsRequest = new Request.Builder().url(System.getProperty("agent.url") + "/metrics").build(); + + // Execute two POST requests + Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + + // Query Prometheus metrics from agent + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .filter(m -> m.contains("status=\"201\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: + // query="insert into person (first_name, last_name, id)" + .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); + } + + private Request makePostRequest(String firstName, String lastName) { + return new Request.Builder() + .url(System.getProperty("deployment.url") + "/people") + .method("POST", RequestBody.create( + MediaType.parse("application/json"), + "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) + .build(); + } +} diff --git a/war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java b/war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java new file mode 100644 index 0000000..a6132ba --- /dev/null +++ b/war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java @@ -0,0 +1,80 @@ +// Copyright 2017 The agent Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package io.agent.it; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class WildflyIT { + + /** + * Run some HTTP queries against a Docker container from image agent/wildfly-kitchensink-agent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. + */ + @Test + public void testWildfly() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); + + // Execute REST call + Response restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "1.0"); + + // Execute REST call again + restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "2.0"); + } + + private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { + + Request metricsRequest = new Request.Builder().url(System.getProperty("agent.url") + "/metrics").build(); + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .filter(m -> m.contains("status=\"200\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); + } +} From 9d39dcb2732c228d15b4a86eb0b6aa3e7071e8c6 Mon Sep 17 00:00:00 2001 From: ANU SAINI <40020900+asaini94@users.noreply.github.com> Date: Mon, 18 Feb 2019 00:08:10 +0530 Subject: [PATCH 27/38] Update JarFilesTest.java --- .../src/test/java/io/agent/agent/JarFilesTest.java | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java b/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java index 0fdac25..4b35a71 100644 --- a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java +++ b/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java @@ -1,17 +1,3 @@ -// Copyright 2017 The agent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package io.agent.agent; import org.junit.jupiter.api.Test; From d0e5b49b3508fb9e23fc8a406433bffb1b650f57 Mon Sep 17 00:00:00 2001 From: anu Date: Mon, 18 Feb 2019 00:25:06 +0530 Subject: [PATCH 28/38] . --- .../agent-framework/agent-agent/pom.xml | 42 -- .../java/io/agent/agent/ClassLoaderCache.java | 65 --- .../main/java/io/agent/agent/JarFiles.java | 127 ------ .../agent/agent/PerDeploymentClassLoader.java | 43 -- .../src/main/java/io/agent/agent/agent.java | 20 - .../java/io/agent/agent/JarFilesTest.java | 59 --- .../agent-framework/agent-api/pom.xml | 21 - .../main/java/io/agent/annotations/After.java | 17 - .../java/io/agent/annotations/Before.java | 17 - .../main/java/io/agent/annotations/Hook.java | 29 -- .../java/io/agent/annotations/Returned.java | 18 - .../java/io/agent/annotations/Thrown.java | 33 -- .../java/io/agent/hookcontext/MetricDef.java | 38 -- .../io/agent/hookcontext/MetricsStore.java | 38 -- .../agent-framework/agent-exporter/pom.xml | 31 -- .../agent/exporter/agentExporterServlet.java | 29 -- .../agent-framework/agent-internal/pom.xml | 69 ---- .../java/io/agent/internal/BuiltInServer.java | 57 --- .../java/io/agent/internal/Delegator.java | 264 ------------ .../java/io/agent/internal/HookException.java | 12 - .../java/io/agent/internal/HookInstance.java | 20 - .../java/io/agent/internal/HookMetadata.java | 128 ------ .../io/agent/internal/HookMetadataParser.java | 321 --------------- .../main/java/io/agent/internal/agent.java | 141 ------- .../java/io/agent/internal/agentAdvice.java | 64 --- .../java/io/agent/internal/jmx/Exporter.java | 27 -- .../io/agent/internal/jmx/ExporterMBean.java | 5 - .../java/io/agent/internal/jmx/Metric.java | 35 -- .../io/agent/internal/jmx/MetricMBean.java | 16 - .../internal/jmx/agentCollectorRegistry.java | 52 --- .../agent-framework/agent-loader/pom.xml | 47 --- .../java/io/agent/loader/agentLoader.java | 87 ---- .../agent-maven-plugin/pom.xml | 73 ---- .../io/agent/plugin/AgentDependencies.java | 165 -------- .../main/java/io/agent/plugin/AgentJar.java | 123 ------ .../java/io/agent/plugin/JarFileNames.java | 63 --- .../io/agent/plugin/ManifestTransformer.java | 51 --- .../main/java/io/agent/plugin/agentMojo.java | 50 --- war&&jar_agent/agent-framework/pom.xml | 85 ---- .../war_agent/pom-with-docker-tests.xml | 386 ------------------ war&&jar_agent/war_agent/pom.xml | 76 ---- .../io/agent/collectors/JmxCollector.java | 36 -- .../main/java/io/agent/hooks/HttpContext.java | 33 -- .../main/java/io/agent/hooks/JdbcHook.java | 129 ------ .../main/java/io/agent/hooks/ServletHook.java | 108 ----- .../src/test/java/io/agent/it/SpringIT.java | 80 ---- .../src/test/java/io/agent/it/WildflyIT.java | 80 ---- 47 files changed, 3510 deletions(-) delete mode 100644 war&&jar_agent/agent-framework/agent-agent/pom.xml delete mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java delete mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/JarFiles.java delete mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java delete mode 100644 war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java delete mode 100644 war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/pom.xml delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java delete mode 100644 war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java delete mode 100644 war&&jar_agent/agent-framework/agent-exporter/pom.xml delete mode 100644 war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/pom.xml delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java delete mode 100644 war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java delete mode 100644 war&&jar_agent/agent-framework/agent-loader/pom.xml delete mode 100644 war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java delete mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml delete mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java delete mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java delete mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/JarFileNames.java delete mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/ManifestTransformer.java delete mode 100644 war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/agentMojo.java delete mode 100644 war&&jar_agent/agent-framework/pom.xml delete mode 100644 war&&jar_agent/war_agent/pom-with-docker-tests.xml delete mode 100644 war&&jar_agent/war_agent/pom.xml delete mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java delete mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java delete mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java delete mode 100644 war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java delete mode 100644 war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java delete mode 100644 war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java diff --git a/war&&jar_agent/agent-framework/agent-agent/pom.xml b/war&&jar_agent/agent-framework/agent-agent/pom.xml deleted file mode 100644 index 97d4714..0000000 --- a/war&&jar_agent/agent-framework/agent-agent/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-agent - agent-agent loaded from the system class loader - - jar - - - - org.junit.jupiter - junit-jupiter-api - test - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - io.agent.agent.agent - io.agent.agent.agent - true - true - true - - - - - - - diff --git a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java deleted file mode 100644 index 70a70b7..0000000 --- a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.agent.agent; - -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * ClassLoaderCache stores the class loaders used for loading the agent modules, - * i.e. agent-hooks, agent-annotations, agent-internals, and their dependencies. - *

- * For the Hooks (like ServletHook or JdbcHook) there is one class loader per deployment, - * because hook classes may reference classes from the deployment, - * e.g. as parameters to the before() and after() methods. - * All other modules and their dependencies are loaded through a shared class loader. - *

- */ -public class ClassLoaderCache { - - private static ClassLoaderCache instance; - - // TODO: The cache does not free class loaders when applications are undeployed. Maybe use WeakHashMap? - private final Map cache = new HashMap<>(); - private final URLClassLoader sharedClassLoader; // shared across multiple deployments - private final List perDeploymentJars; // one class loader for each deployment for these JARs - - private ClassLoaderCache(JarFiles jarFiles) { - sharedClassLoader = new URLClassLoader(pathsToURLs(jarFiles.getSharedJars())); - perDeploymentJars = jarFiles.getPerDeploymentJars(); - } - - public static synchronized ClassLoaderCache getInstance() { - if (instance == null) { - instance = new ClassLoaderCache(JarFiles.extract()); - } - return instance; - } - - public List getPerDeploymentJars() { - return perDeploymentJars; - } - - public synchronized ClassLoader currentClassLoader() { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if (! cache.containsKey(contextClassLoader)) { - cache.put(contextClassLoader, new PerDeploymentClassLoader(pathsToURLs(perDeploymentJars), sharedClassLoader, contextClassLoader)); - } - return cache.get(contextClassLoader); - } - - private static URL[] pathsToURLs(List paths) { - try { - URL[] result = new URL[paths.size()]; - for (int i=0; i perDeploymentJars; // classes loaded for each deployment - private final List sharedJars; // classes shared across multiple deployments - - private JarFiles(List perDeploymentJars, List sharedJars) { - this.perDeploymentJars = Collections.unmodifiableList(perDeploymentJars); - this.sharedJars = Collections.unmodifiableList(sharedJars); - } - - List getPerDeploymentJars() { - return perDeploymentJars; - } - - List getSharedJars() { - return sharedJars; - } - - /** - * Theoretically we could return a list of jar:/ URLs without extracting the JARs, - * but the URLClassLoader has a bug such that jar:/ URLs cannot be used. Therefore, we have - * to extract the JARs and return a list of file:/ URLs. - * See https://bugs.openjdk.java.net/browse/JDK-4735639 - */ - static JarFiles extract() { - List perDeploymentJars = new ArrayList<>(); - List sharedJars = new ArrayList<>(); - Path agentJar = findAgentJar(); - List extractedJars; - try { - Path tmpDir = Files.createTempDirectory("agent-"); - tmpDir.toFile().deleteOnExit(); - extractedJars = unzip(agentJar, tmpDir, entry -> entry.getName().endsWith(".jar")); - } catch (IOException e) { - throw new RuntimeException("Failed to load agent.jar: " + e.getMessage(), e); - } - for (Path jar : extractedJars) { - if (jar.getParent().getFileName().toString().equals("per-deployment-jars")) { - perDeploymentJars.add(jar); - } else { - sharedJars.add(jar); - } - } - return new JarFiles(perDeploymentJars, sharedJars); - } - - private static List unzip(Path jarFile, Path destDir, Predicate filter) throws IOException { - List result = new ArrayList<>(); - try (JarFile agentJar = new JarFile(jarFile.toFile())) { - Enumeration jarEntries = agentJar.entries(); - while (jarEntries.hasMoreElements()) { - JarEntry jarEntry = jarEntries.nextElement(); - if (filter.test(jarEntry)) { - Path destFile = destDir.resolve(jarEntry.getName()); - if (!destFile.getParent().toFile().exists()) { - if (!destFile.getParent().toFile().mkdirs()) { - throw new IOException("Failed to make directory: " + destFile.getParent()); - } - } - Files.copy(agentJar.getInputStream(jarEntry), destFile); - result.add(destFile); - } - } - } - return result; - } - - private static Path findAgentJar() { - CodeSource cs = agent.class.getProtectionDomain().getCodeSource(); - if (cs != null) { - return findAgentJarFromCodeSource(cs); - } else { - // This happens if the agent class is loaded from the bootstrap class loader, - // i.e. in addition to the command line argument -javaagent:/path/to/agent.jar, - // the argument -Xbootclasspath/p:/path/to/agent.jar is used. - return findAgentJarFromCmdline(ManagementFactory.getRuntimeMXBean().getInputArguments()); - } - } - - private static Path findAgentJarFromCodeSource(CodeSource cs) { - try { - return Paths.get(cs.getLocation().toURI()); - } catch (URISyntaxException e) { - throw new RuntimeException("Failed to load agent.jar from " + cs.getLocation() + ": " + e.getMessage(), e); - } - } - - static Path findAgentJarFromCmdline(List cmdlineArgs) { - Pattern p = Pattern.compile("^-javaagent:(.*agent([^/]*).jar)(=.*)?$"); - for (String arg : cmdlineArgs) { - Matcher m = p.matcher(arg); - if (m.matches()) { - return Paths.get(m.group(1)); - } - } - throw new RuntimeException("Failed to locate agent.jar file."); - } -} diff --git a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java deleted file mode 100644 index 7f1ae8f..0000000 --- a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.agent.agent; - -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; - -/** - * Class loader for loading Hooks (like ServletHook or JdbcHook). - *

- * There is one instance per deployment in an application server, because hook classes may - * reference classes from the deployment, e.g. as parameters to the before() and after() methods. - *

- * However, loading shared classes like the Prometheus client library is delegated to the {@link #sharedClassLoader}, - * because the Prometheus metric registry should be accessible across all deployments within an application server. - */ -class PerDeploymentClassLoader extends URLClassLoader { - - private final URLClassLoader sharedClassLoader; // for loading the Prometheus client library - - PerDeploymentClassLoader(URL[] perDeploymentJars, URLClassLoader sharedClassLoader, ClassLoader parent) { - super(perDeploymentJars, parent); - this.sharedClassLoader = sharedClassLoader; - } - - @Override - public Class loadClass(String name) throws ClassNotFoundException { - try { - return sharedClassLoader.loadClass(name); // The Prometheus client library should all have the same initiating loader across deployments. - } catch (ClassNotFoundException e) { - return super.loadClass(name); // Hooks should have different initiating loaders if the context loader differs. - } - } - - // Called by Byte buddy to load the agentAdvice. - @Override - public InputStream getResourceAsStream(String name) { - InputStream result = sharedClassLoader.getResourceAsStream(name); - if (result == null) { - result = super.getResourceAsStream(name); - } - return result; - } -} diff --git a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java b/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java deleted file mode 100644 index f687262..0000000 --- a/war&&jar_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.agent.agent; - -import java.lang.instrument.Instrumentation; - -/** - * We want as little dependencies as possible on the system class loader, - * so the actual agent is loaded in its own class loader and this agent delegates to it. - */ -public class agent { - - public static void premain(String agentArgs, Instrumentation inst) throws Exception { - ClassLoader agentClassLoader = ClassLoaderCache.getInstance().currentClassLoader(); - Class agentClass = agentClassLoader.loadClass("io.agent.internal.agent"); - agentClass.getMethod("premain", String.class, Instrumentation.class).invoke(null, agentArgs, inst); - } - - public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { - premain(agentArgs, inst); - } -} diff --git a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java b/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java deleted file mode 100644 index 0fdac25..0000000 --- a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2017 The agent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.agent.agent; - -import org.junit.jupiter.api.Test; - -import static io.agent.agent.JarFiles.findAgentJarFromCmdline; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.*; - -class JarFilesTest { - - @Test - void testCmdlineParserWildfly() { - // The command line arguments are taken from the Wildfly application server example. - String[] cmdlineArgs = new String[]{ - "-D[Standalone]", - "-Xbootclasspath/p:/tmp/wildfly-10.1.0.Final/modules/system/layers/base/org/jboss/logmanager/main/jboss-logmanager-2.0.4.Final.jar", - "-Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", - "-javaagent:../agent/agent-dist/target/agent.jar=port=9300", - "-Dorg.jboss.boot.log.file=/tmp/wildfly-10.1.0.Final/standalone/log/server.log", - "-Dlogging.configuration=file:/tmp/wildfly-10.1.0.Final/standalone/configuration/logging.properties" - }; - assertEquals("../agent/agent-dist/target/agent.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); - } - - @Test - void testCmdlineParserVersioned() { - String[] cmdlineArgs = new String[] { - "-javaagent:agent-1.0-SNAPSHOT.jar" - }; - assertEquals("agent-1.0-SNAPSHOT.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); - } - - @Test() - void testCmdlineParserFailed() { - String[] cmdlineArgs = new String[] { - "-javaagent:/some/other/agent.jar", - "-jar", - "agent.jar" - }; - Exception e = assertThrows(Exception.class, () -> findAgentJarFromCmdline(asList(cmdlineArgs))); - // The exception should contain some message indicating agent.jar was not found. - assertTrue(e.getMessage().contains("agent.jar")); - } -} diff --git a/war&&jar_agent/agent-framework/agent-api/pom.xml b/war&&jar_agent/agent-framework/agent-api/pom.xml deleted file mode 100644 index d034433..0000000 --- a/war&&jar_agent/agent-framework/agent-api/pom.xml +++ /dev/null @@ -1,21 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-api - agent-api API for implementing hooks - - jar - - - - io.prometheus - simpleclient_common - - - diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java deleted file mode 100644 index c3f73e4..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.agent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The method annotated with @After is executed before exiting the instrumented method. - * The method annotated with @After must have exactly the same parameters as the instrumented method. - * The "method" parameter are the names of the instrumented methods. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface After { - String[] method(); -} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java deleted file mode 100644 index d3cab3b..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.agent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The method annotated with @Before is executed before entering the instrumented method. - * The method annotated with @Before must have exactly the same parameters as the instrumented method. - * The "method" parameter are the names of the instrumented methods. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) -public @interface Before { - String[] method(); -} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java deleted file mode 100644 index a1b0622..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.agent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * The @Hook annotation indicates that the annotated class is a Hook. - * The parameter {@link Hook#instruments()} defines which classes or interfaces are instrumented by that Hook. - * The method annotations {@link Before} and {@link After} define which methods should be instrumented within that class or interface. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface Hook { - - /** - * List of classes or interfaces to be instrumented. - */ - String[] instruments(); - - /** - * If true, nested calls are skipped. - * If set to {@code false}, nested calls will also be instrumented. - * For nested calls, the same Hook instance is re-used. - * For outer calls, a new Hook instance is created. - */ - boolean skipNestedCalls() default true; -} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java deleted file mode 100644 index 12033c4..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.agent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * A parameter annotated with @Returned can be used in a Hook's @After method to capture the return value of the instrumented method. - * - * Example: In order to instrument the following method: - * The parameter annotated with @Returned is optional, if the hook does not use the return value, the parameter can be omitted. - * If the instrumented method terminates exceptionally, the type's default value is assigned to the parameter, - * i.e. {@code 0} for numeric types and {@code null} for reference types. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Returned {} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java deleted file mode 100644 index c82ccd5..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.agent.annotations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * A parameter annotated with @Thrown can be used in a Hook's @After method to capture an Exception thrown in an instrumented method. - * - * Example: In order to instrument the following method: - * - *

- *     int div(int a, int b) {
- *         return a / b;
- *     }
- * 
- * - * A Hook could use an @After method like this: - * - *
- *    {@literal @}After(method = "div")
- *     void after(int a, int b, @Returned int result, @Thrown Throwable exception) {...}
- * 
- * - * In case everything goes well, the {@code result} will be the return value of {@code div()}, and {@code excption} will be {@code null}. - * If {@code b} is {@code 0}, the {@code result} will be {@code 0}, and {@code exception} will be an {@link ArithmeticException} (division by zero). - *

- * The parameter annotated with @Thrown is optional, if the hook does not use the exception, the parameter can be omitted. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.PARAMETER) -public @interface Thrown {} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java deleted file mode 100644 index 42655b8..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.agent.hookcontext; - -import io.prometheus.client.Collector; -import io.prometheus.client.CollectorRegistry; - -import java.util.function.BiFunction; - -/** - * See {@link #MetricDef(String, BiFunction)} - */ -public class MetricDef { - - private final String metricName; - private final BiFunction producer; - - /** - * See {@link MetricsStore}. - * @param metricName Name of the Prometheus metric, like "http_requests_total". - * @param producer Function to create a new metric and register it with the registry. - * This function will only be called once, subsequent calls to {@link MetricsStore#createOrGet(MetricDef)} - * will return the previously created metric with the specified name. - * The two parameters are the metricName as specified above, - * and the Prometheus registry where the new metric should be registered. - * For an example see JavaDoc for {@link MetricsStore}. - */ - public MetricDef(String metricName, BiFunction producer) { - this.metricName = metricName; - this.producer = producer; - } - - String getMetricName() { - return metricName; - } - - BiFunction getProducer() { - return producer; - } -} diff --git a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java b/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java deleted file mode 100644 index f14f085..0000000 --- a/war&&jar_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.agent.hookcontext; - -import io.prometheus.client.Collector; -import io.prometheus.client.CollectorRegistry; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.BiFunction; - -/** - * Instead of creating Prometheus Metrics directly, Hooks should use the {@link MetricsStore} like this: - *

- * Counter httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>(
- *         "http_requests_total",
- *         (name, registry) -> Counter.build()
- *                 .name(name)
- *                 .labelNames("method", "path", "status")
- *                 .help("Total number of http requests.")
- *                 .register(registry)
- * ));
- * 
- * The Promgent framework will take care that each metric is created only once and re-used across re-deployments in an application server. - */ -public class MetricsStore { - - private final CollectorRegistry registry; - private final ConcurrentMap metrics = new ConcurrentHashMap<>(); - - public MetricsStore(CollectorRegistry registry) { - this.registry = registry; - } - - // See {@link MetricsStore} and {@link MetricDef#MetricDef(String, BiFunction)}. - @SuppressWarnings("unchecked") - public T createOrGet(MetricDef metricDef) { - return (T) metrics.computeIfAbsent(metricDef.getMetricName(), s -> metricDef.getProducer().apply(metricDef.getMetricName(), registry)); - } -} diff --git a/war&&jar_agent/agent-framework/agent-exporter/pom.xml b/war&&jar_agent/agent-framework/agent-exporter/pom.xml deleted file mode 100644 index dd57d88..0000000 --- a/war&&jar_agent/agent-framework/agent-exporter/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-exporter - agent.war - - - false - - - war - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - agent - - diff --git a/war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java b/war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java deleted file mode 100644 index b8d2ac7..0000000 --- a/war&&jar_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.agent.exporter; - -import javax.management.ObjectName; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.lang.management.ManagementFactory; - -/** - * This servlet simply calls the ExporterMBean via JMX and provides the result. - */ -@WebServlet("/") -public class agentExporterServlet extends HttpServlet { - - @Override - public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { - try { - String result = (String) ManagementFactory.getPlatformMBeanServer() - .getAttribute(new ObjectName("io.agent:type=exporter"), "TextFormat"); - response.getWriter().println(result); - } catch (Exception e) { - response.setStatus(500); - response.getWriter().println("Failed to load Exporter MBean. Are you sure the agent is running?"); - e.printStackTrace(); - } - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/pom.xml b/war&&jar_agent/agent-framework/agent-internal/pom.xml deleted file mode 100644 index 25e1e4f..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/pom.xml +++ /dev/null @@ -1,69 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-internal - agent-internal classes shared across deployments - - jar - - - - - ${project.groupId} - agent-agent - ${project.version} - provided - - - - ${project.groupId} - agent-api - ${project.version} - provided - - - - net.bytebuddy - byte-buddy - 1.9.4 - - - - commons-io - commons-io - 2.6 - - - - io.prometheus - simpleclient_common - - - - org.junit.jupiter - junit-jupiter-api - test - - - - org.mockito - mockito-core - 2.23.0 - test - - - - javax.servlet - javax.servlet-api - 4.0.1 - test - - - - diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java deleted file mode 100644 index d3bb002..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java +++ /dev/null @@ -1,57 +0,0 @@ - - -package io.agent.internal; - -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpServer; -import io.prometheus.client.CollectorRegistry; -import io.prometheus.client.exporter.common.TextFormat; - -import java.io.IOException; -import java.io.StringWriter; -import java.net.InetSocketAddress; -import java.util.Collections; - -/** - * Use the Java runtime's built-in {@link HttpServer} to export Prometheus metrics. - */ -class BuiltInServer { - - static void run(String host, String portString, CollectorRegistry registry) throws Exception { - try { - int port = Integer.parseInt(portString); - InetSocketAddress address = host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port); - HttpServer httpServer = HttpServer.create(address, 10); - httpServer.createContext("/", httpExchange -> { - if ("/metrics".equals(httpExchange.getRequestURI().getPath())) { - respondMetrics(registry, httpExchange); - } else { - respondRedirect(httpExchange); - } - }); - httpServer.start(); - } catch (NumberFormatException e) { - throw new RuntimeException("Failed to parse command line arguments: '" + portString + "' is not a valid port number."); - } - } - - private static void respondMetrics(CollectorRegistry registry, HttpExchange httpExchange) throws IOException { - StringWriter respBodyWriter = new StringWriter(); - respBodyWriter.write("# Metrics will become visible when they are updated for the first time.\n"); - TextFormat.write004(respBodyWriter, registry.metricFamilySamples()); - byte[] respBody = respBodyWriter.toString().getBytes("UTF-8"); - httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); - httpExchange.sendResponseHeaders(200, respBody.length); - httpExchange.getResponseBody().write(respBody); - httpExchange.getResponseBody().close(); - } - - private static void respondRedirect(HttpExchange httpExchange) throws IOException { - byte[] respBody = "Metrics are provided on the /metrics endpoint.".getBytes("UTF-8"); - httpExchange.getResponseHeaders().add("Location", "/metrics"); - httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); - httpExchange.sendResponseHeaders(302, respBody.length); - httpExchange.getResponseBody().write(respBody); - httpExchange.getResponseBody().close(); - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java deleted file mode 100644 index de5dd09..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java +++ /dev/null @@ -1,264 +0,0 @@ - - -package io.agent.internal; - -import io.agent.agent.ClassLoaderCache; -import io.agent.annotations.After; -import io.agent.annotations.Before; -import io.agent.annotations.Returned; -import io.agent.annotations.Thrown; -import io.agent.hookcontext.MetricsStore; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * Delegator is called from the Byte Buddy Advice, and calls the Hook's @Before and @After methods. - *

- * TODO: This is called often, should be performance optimized, e.g. caching hook method handles, etc. - */ -public class Delegator { - - private static Delegator instance; // not thread-safe, but it is set only once in the agent's premain method. - - private final SortedSet hookMetadata; - private final MetricsStore metricsStore; - private final ClassLoaderCache classLoaderCache; - private final ThreadLocal, Object>> threadLocal; - - private Delegator(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { - this.hookMetadata = hookMetadata; - this.metricsStore = metricsStore; - this.classLoaderCache = classLoaderCache; - this.threadLocal = ThreadLocal.withInitial(HashMap::new); - } - - public static void init(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { - instance = new Delegator(hookMetadata, metricsStore, classLoaderCache); - } - - /** - * Should be called from the Advice's @OnMethodEnter method. Returns the list of Hooks to be passed on to after() - */ - public static List before(Class interceptedClass, Method interceptedMethod, Object[] args) { - return instance.doBefore(interceptedClass, interceptedMethod, args); - } - - private List doBefore(Class interceptedClass, Method interceptedMethod, Object[] args) { - List hookInstances = loadFromThreadLocalOrCreate(interceptedClass, interceptedMethod); - for (HookInstance hookInstance : hookInstances) { - invokeBefore(hookInstance.getInstance(), interceptedMethod, args); - } - return hookInstances; - } - - /** - * Should be called from the Advice's @OnMethodExit method. First parameter is the list of hooks returned by before() - */ - public static void after(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { - instance.doAfter(hookInstances, interceptedMethod, args, returned, thrown); - } - - private void doAfter(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { - if (hookInstances != null) { - for (HookInstance hookInstance : hookInstances) { - invokeAfter(hookInstance.getInstance(), interceptedMethod, args, returned, thrown); - if (!hookInstance.isRecursiveCall()) { - threadLocal.get().remove(hookInstance.getInstance().getClass()); - } - } - } - } - - private List loadFromThreadLocalOrCreate(Class interceptedClass, Method interceptedMethod) { - return hookMetadata.stream() - .filter(hook -> classOrInterfaceMatches(interceptedClass, hook)) - .filter(hook -> methodNameAndNumArgsMatch(interceptedMethod, hook)) - .map(hook -> loadHookClass(hook)) - .filter(hookClass -> argumentTypesMatch(hookClass, interceptedMethod)) - .filter(hookClass -> ! shouldBeSkipped(hookClass)) - .map(hookClass -> loadFromTheadLocalOrCreate(hookClass)) - .collect(Collectors.toList()); - } - - private static boolean classOrInterfaceMatches(Class classToBeInstrumented, HookMetadata hook) { - Set classesAndInterfaces = getAllSuperClassesAndInterfaces(classToBeInstrumented); - return hook.getInstruments().stream().anyMatch(classesAndInterfaces::contains); - } - - private static Set getAllSuperClassesAndInterfaces(Class clazz) { - Set result = new HashSet<>(); - addAllSuperClassesAndInterfaces(clazz, result); - return result; - } - - private static void addAllSuperClassesAndInterfaces(Class clazz, Set result) { - if (clazz == null) { - return; - } - if (result.contains(clazz.getName())) { - return; - } - result.add(clazz.getName()); - for (Class ifc : clazz.getInterfaces()) { - addAllSuperClassesAndInterfaces(ifc, result); - } - addAllSuperClassesAndInterfaces(clazz.getSuperclass(), result); - } - - private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata hook) { - return hook.getMethods().stream() - .anyMatch(instrumentedMethod -> methodNameAndNumArgsMatch(interceptedMethod, instrumentedMethod)); - } - - private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata.MethodSignature instrumentedMethod) { - if (!interceptedMethod.getName().equals(instrumentedMethod.getMethodName())) { - return false; - } - if (interceptedMethod.getParameterCount() != instrumentedMethod.getParameterTypes().size()) { - return false; - } - return true; - } - - private Class loadHookClass(HookMetadata hook) { - try { - return classLoaderCache.currentClassLoader().loadClass(hook.getHookClassName()); - } catch (ClassNotFoundException e) { - throw new HookException("Failed to load Hook class " + hook.getHookClassName() + ": " + e.getMessage(), e); - } - } - - private static boolean argumentTypesMatch(Class hookClass, Method interceptedMethod) { - List before = findHookMethods(Before.class, hookClass, interceptedMethod); - List after = findHookMethods(After.class, hookClass, interceptedMethod); - return ! (before.isEmpty() && after.isEmpty()); - } - - private static List findHookMethods(Class annotation, Class hookClass, Method interceptedMethod) throws HookException { - return Stream.of(hookClass.getDeclaredMethods()) - .filter(method -> method.isAnnotationPresent(annotation)) - .filter(method -> getMethodNames(method.getAnnotation(annotation)).contains(interceptedMethod.getName())) - .filter(method -> parameterTypesMatch(method, interceptedMethod)) - .collect(Collectors.toList()); - } - - private static List getMethodNames(Annotation annotation) throws HookException { - if (Before.class.isAssignableFrom(annotation.getClass())) { - return Arrays.asList(((Before) annotation).method()); - } else if (After.class.isAssignableFrom(annotation.getClass())) { - return Arrays.asList(((After) annotation).method()); - } else { - throw new HookException("Unsupported Annotation: @" + annotation.getClass().getSimpleName() + "."); - } - } - - // TODO: We could extend this to find the "closest" match, like in Java method calls. - private static boolean parameterTypesMatch(Method hookMethod, Method interceptedMethod) { - List> hookParameterTypes = stripReturnedAndThrown(hookMethod); - if (hookParameterTypes.size() != interceptedMethod.getParameterCount()) { - return false; - } - for (int i = 0; i < hookParameterTypes.size(); i++) { - Class hookParam = hookParameterTypes.get(i); - Class interceptedParam = interceptedMethod.getParameterTypes()[i]; - if (!hookParam.equals(interceptedParam)) { - return false; - } - } - return true; - } - - private static List> stripReturnedAndThrown(Method hookMethod) { - Class[] allTypes = hookMethod.getParameterTypes(); - Annotation[][] annotations = hookMethod.getParameterAnnotations(); - if (allTypes.length != annotations.length) { - throw new HookException("Method.getParameterAnnotations() returned an unexpected value. This is a bug in agent."); - } - List> result = new ArrayList<>(); - for (int i=0; i Returned.class.equals(a) || Thrown.class.equals(a))) { - result.add(allTypes[i]); - } - } - return result; - } - - private boolean shouldBeSkipped(Class hookClass) { - return hookClass.getAnnotation(io.agent.annotations.Hook.class).skipNestedCalls() - && threadLocal.get().containsKey(hookClass); - } - - private HookInstance loadFromTheadLocalOrCreate(Class hookClass) { - Object existingHookInstance = threadLocal.get().get(hookClass); - if (existingHookInstance != null) { - return new HookInstance(existingHookInstance, true); - } else { - String errMsg = "Failed to create new instance of hook " + hookClass.getSimpleName() + ": "; - try { - Object newHookInstance = hookClass.getConstructor(MetricsStore.class).newInstance(metricsStore); - threadLocal.get().put(hookClass, newHookInstance); - return new HookInstance(newHookInstance, false); - } catch (NoSuchMethodException e) { - throw new HookException(errMsg + "Hook classes must have a public constructor with a single parameter of type " + MetricsStore.class.getSimpleName(), e); - } catch (Exception e) { - throw new HookException(errMsg + e.getMessage(), e); - } - } - } - - /** - * Invoke the matching Hook methods annotated with @Before - */ - private static void invokeBefore(Object hookInstance, Method interceptedMethod, Object[] args) throws HookException { - invoke(Before.class, hookInstance, interceptedMethod, args, null, null); - } - - /** - * Invoke the matching Hook methods annotated with @After - */ - private static void invokeAfter(Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { - invoke(After.class, hookInstance, interceptedMethod, args, returned, thrown); - } - - private static void invoke(Class annotation, Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { - if (args.length != interceptedMethod.getParameterCount()) { - throw new IllegalArgumentException("Number of provided arguments is " + args.length + ", but interceptedMethod expects " + interceptedMethod.getParameterCount() + " argument(s)."); - } - for (Method method : findHookMethods(annotation, hookInstance.getClass(), interceptedMethod)) { - try { - if (!method.isAccessible()) { - method.setAccessible(true); - } - method.invoke(hookInstance, addReturnedAndThrownArgs(method, args, returned, thrown)); - } catch (Exception e) { - throw new HookException("Failed to call " + method.getName() + "() on " + hookInstance.getClass().getSimpleName() + ": " + e.getMessage(), e); - } - } - } - - private static Object[] addReturnedAndThrownArgs(Method hookMethod, Object[] args, Object returned, Throwable thrown) { - Annotation[][] annotations = hookMethod.getParameterAnnotations(); - List result = new ArrayList<>(); - int arg = 0; - for (Annotation[] annotation : annotations) { - if (Arrays.stream(annotation) - .map(Annotation::annotationType) - .anyMatch(Returned.class::equals)) { - result.add(returned); - } else if (Arrays.stream(annotation) - .map(Annotation::annotationType) - .anyMatch(Thrown.class::equals)) { - result.add(thrown); - } else { - result.add(args[arg++]); - } - } - return result.toArray(); - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java deleted file mode 100644 index dfaf80c..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.agent.internal; - -public class HookException extends RuntimeException { - - public HookException(String message) { - super(message); - } - - public HookException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java deleted file mode 100644 index 73c2f20..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.agent.internal; - -public class HookInstance { - - private final Object instance; - private final boolean isRecursiveCall; // true if we have taken an existing instance from the ThreadLocal - - public HookInstance(Object instance, boolean isRecursiveCall) { - this.instance = instance; - this.isRecursiveCall = isRecursiveCall; - } - - public Object getInstance() { - return instance; - } - - public boolean isRecursiveCall() { - return isRecursiveCall; - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java deleted file mode 100644 index 21652d5..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java +++ /dev/null @@ -1,128 +0,0 @@ - -package io.agent.internal; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * Metadata for a Hook class as parsed from the @Hook, @Before, and @After annotations. - */ -public class HookMetadata implements Comparable { - - private final String hookClassName; - private final SortedSet instruments; - private final SortedSet methods; - - public static class MethodSignature implements Comparable { - - private final String methodName; - private final List parameterTypes; - - public MethodSignature(String methodName, List parameterTypes) { - this.methodName = methodName; - this.parameterTypes = Collections.unmodifiableList(new ArrayList<>(parameterTypes)); - } - - public String getMethodName() { - return methodName; - } - - public List getParameterTypes() { - return parameterTypes; - } - - @Override - public String toString() { - return methodName + "(" + String.join(", ", parameterTypes) + ")"; - } - - @Override - public boolean equals(Object o) { - return o != null && getClass() == o.getClass() && compareTo((MethodSignature) o) == 0; - } - - @Override - public int hashCode() { - return Objects.hash(toString()); - } - - @Override - public int compareTo(MethodSignature o) { - return Comparator - .comparing(MethodSignature::getMethodName) - .thenComparing(MethodSignature::getParameterTypes, new LexicographicalComparator<>()) - .compare(this, o); - } - } - - public HookMetadata(String hookClassName, Collection instruments, Collection methods) { - this.hookClassName = hookClassName; - this.instruments = Collections.unmodifiableSortedSet(new TreeSet<>(instruments)); - this.methods = Collections.unmodifiableSortedSet(new TreeSet<>(methods)); - } - - public String getHookClassName() { - return hookClassName; - } - - public SortedSet getInstruments() { - return instruments; - } - - public SortedSet getMethods() { - return methods; - } - - @Override - public String toString() { // TODO: instruments is a Set - String delimiter = System.lineSeparator() + " * "; - return hookClassName + " instruments [" + String.join(", ", instruments) + "]:" + delimiter + String.join(delimiter, strings(methods)); - } - - @Override - public boolean equals(Object o) { - return o != null && getClass() == o.getClass() && compareTo((HookMetadata) o) == 0; - } - - @Override - public int hashCode() { - return Objects.hash(toString()); - } - - @Override - public int compareTo(HookMetadata o) { - return Comparator - .comparing(HookMetadata::getHookClassName) - .thenComparing(HookMetadata::getInstruments, new LexicographicalComparator<>()) - .thenComparing(HookMetadata::getMethods, new LexicographicalComparator<>()) - .compare(this, o); - } - - private List strings(Collection list) { - return list.stream().map(Object::toString).collect(Collectors.toList()); - } - - /** - * Compare SortedSet instances or List instances in lexicographical order. - */ - static class LexicographicalComparator, T extends Iterable> implements Comparator { - @Override - public int compare(T list1, T list2) { - Iterator iterator1 = list1.iterator(); - Iterator iterator2 = list2.iterator(); - while (iterator1.hasNext() && iterator2.hasNext()) { - int result = iterator1.next().compareTo(iterator2.next()); - if (result != 0) { - return result; - } - } - if (iterator1.hasNext()) { - return 1; - } - if (iterator2.hasNext()) { - return -1; - } - return 0; - } - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java deleted file mode 100644 index 2723b10..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java +++ /dev/null @@ -1,321 +0,0 @@ -package io.agent.internal; - -import io.agent.annotations.After; -import io.agent.annotations.Before; -import io.agent.annotations.Returned; -import io.agent.annotations.Thrown; -import io.agent.internal.HookMetadata.MethodSignature; -import net.bytebuddy.jar.asm.*; -import org.apache.commons.io.IOUtils; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -/** - * Inspect all hook classes in a JAR file and load the metadata from the @Hook, @Before, and @After annotations. - *

- * The tricky part is that we cannot create an instance of the hook classes, because they might reference - * classes that are not available in the agent's premain phase. We need to parse the metadata without instantiating - * the hook classes. - */ -public class HookMetadataParser { - - private final SortedSet hookJars; - - public HookMetadataParser(Collection hookJars) { - this.hookJars = Collections.unmodifiableSortedSet(new TreeSet<>(hookJars)); - } - - /** - * Get the {@link HookMetadata} for the hook classes without loading the hook classes. - *

- * We unsuccessfully tried two alternative implementations before we ended up with the current one. - * The first attempt was to use reflection like this: - *

-     *     Class hookClass = HookConfig.class.getClassLoader().loadClass(hookClassName);
-     *     for (Method m : hookClass.getMethods()) {
-     *         // ... get methodConfig
-     *     }
-     * 
- * However, this throws a ClassNotFoundException in hookClass.getMethods(), because - * a hook method may have parameters like ServletRequest that are not available during agent startup. - *

- * The second attempt was to analyze the hook using Byte buddy like this: - *

-     *     TypeDescription typeDesc = typePool.describe(hookClassName).resolve();
-     *     for (MethodDescription methodDesc : typeDesc.getDeclaredMethods()) {
-     *         TypeList params = methodDesc.getParameters().asTypeList().asErasures();
-     *         // ... get methodConfig
-     *     }
-     * 
- * However, this throws a ClassNotFoundException in methodDesc.getParameters().asTypeList(). - * We did not find any way in the Byte buddy API to access a method's parameter types without loading these types. - *

- * The current implementation uses ASM directly to analyze the hook class's byte code. - */ - SortedSet parse() throws IOException, ClassNotFoundException { - return parse(className -> true); // Parse all classes found in the JAR files. - } - - /** - * See {@link #parse()}. - * - * The classNameFilter is used to parse only specific classes from the JAR files. - */ - public SortedSet parse(Predicate classNameFilter) throws IOException, ClassNotFoundException { - SortedSet result = new TreeSet<>(); - for (String className : listAllJavaClasses(hookJars, classNameFilter)) { - byte[] binaryRepresentation = readBinaryRepresentation(className); - ClassReader classReader = new ClassReader(binaryRepresentation); - HookMetadataBuilder hookMetadata = new HookMetadataBuilder(className); - classReader.accept(new ClassVisitor(Opcodes.ASM5) { - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (visible && typeEquals(desc, io.agent.annotations.Hook.class)) { - return new AnnotationValueCollector("instruments", hookMetadata::addInstruments, Opcodes.ASM5, super.visitAnnotation(desc, visible)); - } else { - return super.visitAnnotation(desc, visible); - } - } - - @Override - public MethodVisitor visitMethod(int i, String method, String desc, String signature, String[] strings) { - List parameterTypes = Arrays.stream(Type.getArgumentTypes(desc)) - .map(Type::getClassName) - .collect(Collectors.toList()); - MethodSignatureBuilder builder = hookMetadata.newMethodSignature(parameterTypes); - return new MethodVisitor(Opcodes.ASM5, super.visitMethod(i, method, desc, signature, strings)) { - @Override - public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { - if (visible && typeEquals(desc, Returned.class, Thrown.class)) { - builder.markReturnedOrThrown(parameter); - } - return super.visitParameterAnnotation(parameter, desc, visible); - } - - @Override - public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - if (visible && typeEquals(desc, Before.class, After.class)) { - return new AnnotationValueCollector("method", builder::addMethodName, Opcodes.ASM5, super.visitAnnotation(desc, visible)); - } else { - return super.visitAnnotation(desc, visible); - } - } - }; - } - - }, ClassReader.SKIP_CODE); - if (hookMetadata.isComplete()) { - result.add(hookMetadata.build()); - } - } - return result; - } - - private byte[] readBinaryRepresentation(String className) throws ClassNotFoundException { - String classFileName = "/" + className.replace(".", "/") + ".class"; - try(InputStream stream = getResourceAsStream(classFileName)) { - if (stream == null) { - throw new ClassNotFoundException(className); - } - return IOUtils.toByteArray(stream); - } catch (IOException e) { - throw new ClassNotFoundException(className, e); - } - } - - private InputStream getResourceAsStream(String name) throws IOException { - for (Path hookJar : hookJars) { - try { - URL url; - // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. - if (Files.isDirectory(hookJar)) { - url = hookJar.toUri().resolve("." + name).toURL(); - } else if (Files.isRegularFile(hookJar)) { - url = new URL("jar:" + hookJar.toUri().toURL().toString() + "!" + name); - } else { - throw new IOException("Invalid JAR file or classes directory: " + hookJar); - } - return url.openStream(); - } catch (FileNotFoundException e) {} - } - throw new FileNotFoundException(name + " not found in [" + hookJars.stream().map(Path::toString).reduce("", (s1, s2) -> s1 + ", " + s2) + "]"); - } - - private boolean typeEquals(String typeDescriptor, Class... classes) { - for (Class clazz : classes) { - if (clazz.getName().equals(Type.getType(typeDescriptor).getClassName())) { - return true; - } - } - return false; - } - - - /** - * Helper to create {@link HookMetadata} isntances. - */ - private static class HookMetadataBuilder { - - private String hookClassName; - private List instruments = new ArrayList<>(); - private List methods = new ArrayList<>(); - - private HookMetadataBuilder(String hookClassName) { - this.hookClassName = hookClassName; - } - - private void addInstruments(String instruments) { - this.instruments.add(instruments); - } - - private MethodSignatureBuilder newMethodSignature(List parameterTypes) { - MethodSignatureBuilder builder = new MethodSignatureBuilder(); - for (String parameterType : parameterTypes) { - builder.addParameterType(parameterType); - } - methods.add(builder); - return builder; - } - - private HookMetadata build() { - SortedSet methodSignatures = new TreeSet<>(); - for (MethodSignatureBuilder builder : methods) { - methodSignatures.addAll(builder.build()); - } - return new HookMetadata(hookClassName, instruments, methodSignatures); - } - - private boolean isComplete() { - return hookClassName != null && instruments.size() > 0 && methods.size() > 0; - } - } - - /** - * Helper to create {@link MethodSignature} instances. - *

- * There's a slight mismatch between a {@link MethodSignatureBuilder} and a {@link MethodSignature}: - * While a {@link MethodSignature} can only contain a single method name, - * the {@link MethodSignatureBuilder} may have multiple method names. - * Therefore, the {@link #build()} method might create multiple {@link MethodSignature} instances - * out of a single {@link MethodSignatureBuilder}. - */ - private static class MethodSignatureBuilder { - - private static class ParameterType { - final String type; - boolean isReturnedOrThrown; // method parameters annotated with @Returned or @Thrown will be ignored. - - private ParameterType(String type) { - this.type = type; - this.isReturnedOrThrown = false; - } - } - - SortedSet methodNames = new TreeSet<>(); - List parameterTypes = new ArrayList<>(); - - private void addMethodName(String methodName) { - methodNames.add(methodName); - } - - private void addParameterType(String parameterType) { - parameterTypes.add(new ParameterType(parameterType)); - } - - private SortedSet build() { - List strippedParameterTypes = parameterTypes.stream() - .filter(p -> ! p.isReturnedOrThrown) - .map(p -> p.type) - .collect(Collectors.toList()); - SortedSet result = new TreeSet<>(); - for (String methodName : methodNames) { - result.add(new MethodSignature(methodName, strippedParameterTypes)); - } - return result; - } - - public void markReturnedOrThrown(int parameter) { - // We know that parameter is a valid index in parameterTypes. - parameterTypes.get(parameter).isReturnedOrThrown = true; - } - } - - /** - * Collect annotation values from @Hook(instruments = ...) or @Before(method = ...) or @After(method = ...) - *

- * Example: If the annotation is @Before(method={"service", "doFilter"}) - * then using a AnnotationValueCollector with methodName=="method" will add the call - * consumer.accept("service") and consumer.accept("doFilter") - */ - private static class AnnotationValueCollector extends AnnotationVisitor { - private final Consumer consumer; - private final String methodName; - private AnnotationValueCollector(String methodName, Consumer consumer, int api, AnnotationVisitor av) { - super(api, av); - this.consumer = consumer; - this.methodName = methodName; - } - @Override - public AnnotationVisitor visitArray(String name) { - if (methodName.equals(name)) { - return new AnnotationVisitor(Opcodes.ASM5, super.visitArray(name)) { - @Override - public void visit(String name, Object value) { - consumer.accept(value.toString()); - } - }; - } else { - return super.visitArray(name); - } - } - } - - /** - * List all Java classes found in the JAR files. - * - */ - private static Set listAllJavaClasses(Set hookJars, Predicate classNameFilter) throws IOException { - Set result = new TreeSet<>(); - for (Path hookJar : hookJars) { - // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. - if (hookJar.toFile().isDirectory()) { - try (Stream dirEntries = Files.walk(hookJar)) { - addClassNames(dirEntries.map(hookJar::relativize).map(Path::toString), result, classNameFilter); - } - } - else if (hookJar.toFile().isFile()) { - try (ZipFile zipFile = new ZipFile(hookJar.toFile())) { - addClassNames(zipFile.stream().map(ZipEntry::getName), result, classNameFilter); - } - } else { - throw new IOException(hookJar + ": Failed to read file or directory."); - } - } - return result; - } - - /** - * Convert class file paths to class names and add them to result. - */ - private static void addClassNames(Stream paths, Collection result, Predicate classNameFilter) { - paths - .filter(name -> name.endsWith(".class")) - .map(name -> name.substring(0, name.length() - ".class".length())) - .map(name -> name.startsWith("/") ? name.substring(1) : name) - .map(name -> name.replace("/", ".")) - .filter(classNameFilter) - .collect(Collectors.toCollection(() -> result)); - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java deleted file mode 100644 index 4e9ac04..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.agent.internal; - -import static net.bytebuddy.matcher.ElementMatchers.isAbstract; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.not; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import javax.management.ObjectName; -import java.lang.instrument.Instrumentation; -import java.lang.management.ManagementFactory; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; - -import io.agent.agent.ClassLoaderCache; -import io.agent.hookcontext.MetricsStore; -import io.agent.internal.HookMetadata.MethodSignature; -import io.agent.internal.jmx.Exporter; -import io.agent.internal.jmx.agentCollectorRegistry; -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.description.method.MethodDescription; -import net.bytebuddy.matcher.ElementMatcher; -import net.bytebuddy.matcher.ElementMatchers; - -public class agent { - - public static void premain(String agentArgs, Instrumentation inst) { - try { - agentCollectorRegistry registry = new agentCollectorRegistry(); - ManagementFactory.getPlatformMBeanServer().registerMBean(new Exporter(registry), new ObjectName("io.agent:type=exporter")); - Map args = parseCmdline(agentArgs); - if (args.containsKey("port")) { - BuiltInServer.run(args.get("host"), args.get("port"), registry); - } - ClassLoaderCache classLoaderCache = ClassLoaderCache.getInstance(); - List hookJars = classLoaderCache.getPerDeploymentJars(); - SortedSet hookMetadata = new HookMetadataParser(hookJars).parse(); - MetricsStore metricsStore = new MetricsStore(registry); - Delegator.init(hookMetadata, metricsStore, classLoaderCache); - printHookMetadata(hookMetadata); - - AgentBuilder agentBuilder = new AgentBuilder.Default(); - agentBuilder = applyHooks(agentBuilder, hookMetadata, classLoaderCache); - agentBuilder - .disableClassFormatChanges() - // .with(AgentBuilder.Listener.StreamWriting.toSystemError()) // use this to see exceptions thrown in instrumented code - .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) - .with(AgentBuilder.TypeStrategy.Default.REDEFINE) - .installOn(inst); - - // TODO -- the following is an experiment supporting collectors directly (in addition to hooks) - // io.prometheus.client.Collector jmxCollector = (io.prometheus.client.Collector) classLoaderCache.currentClassLoader().loadClass("io.agent.collectors.JmxCollector").newInstance(); - // registry.registerNoJmx(jmxCollector); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - /** - * Add {@link ElementMatcher} for the hooks. - */ - private static AgentBuilder applyHooks(AgentBuilder agentBuilder, SortedSet hookMetadata, ClassLoaderCache classLoaderCache) { - Map> instruments = getInstruments(hookMetadata); - for (Map.Entry> entry : instruments.entrySet()) { - String instrumentedClassName = entry.getKey(); - Set instrumentedMethods = entry.getValue(); - agentBuilder = agentBuilder - .type(ElementMatchers.hasSuperType(named(instrumentedClassName))) - .transform(new AgentBuilder.Transformer.ForAdvice() - .include(classLoaderCache.currentClassLoader()) // must be able to load agentAdvice - .advice(matchAnyMethodIn(instrumentedMethods), agentAdvice.class.getName()) - ); - } - return agentBuilder; - } - - /** - * key: name of instrumented class or interface, value: set of instrumented methods for that class or interface - */ - public static Map> getInstruments(Set hooks) { - Map> result = new TreeMap<>(); - for (HookMetadata hookMetadata : hooks) { - for (String instruments : hookMetadata.getInstruments()) { - if (!result.containsKey(instruments)) { - result.put(instruments, new TreeSet<>()); - } - result.get(instruments).addAll(hookMetadata.getMethods()); - } - } - return result; - } - - /** - * Returns a byte buddy matcher matching any method contained in methodSignatures. - */ - public static ElementMatcher matchAnyMethodIn(Set methodSignatures) { - ElementMatcher.Junction methodMatcher = ElementMatchers.none(); - for (MethodSignature methodSignature : methodSignatures) { - ElementMatcher.Junction junction = ElementMatchers - .named(methodSignature.getMethodName()) - .and(not(isAbstract())) - .and(takesArguments(methodSignature.getParameterTypes().size())); - for (int i = 0; i < methodSignature.getParameterTypes().size(); i++) { - junction = junction.and(takesArgument(i, named(methodSignature.getParameterTypes().get(i)))); - } - methodMatcher = methodMatcher.or(junction); - } - return methodMatcher; - } - - /** - * Parse a comma-separated list of key/value pairs. Example: "host=localhost,port=9300" - */ - private static Map parseCmdline(String agentArgs) { - Map result = new HashMap<>(); - if (agentArgs != null) { - for (String keyValueString : agentArgs.split(",")) { - String[] keyValue = keyValueString.split("="); - if (keyValue.length != 2) { - throw new RuntimeException("Failed to parse command line arguments '" + agentArgs + "'. " + - "Expecting a comma-separated list of key/value pairs, as for example 'host=localhost,port=9300'."); - } - result.put(keyValue[0], keyValue[1]); - } - } - return result; - } - - private static void printHookMetadata(SortedSet hookMetadata) { - System.out.println("agent instrumenting the following classes or interfaces:"); - for (HookMetadata m : hookMetadata) { - System.out.println(m); - } - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java deleted file mode 100644 index 8566f3d..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.agent.internal; - -import io.agent.agent.ClassLoaderCache; -import net.bytebuddy.implementation.bytecode.assign.Assigner; - -import java.lang.reflect.Method; -import java.util.List; - -import static net.bytebuddy.asm.Advice.*; - -public class agentAdvice { - - // TODO: - // Should we move this class into it's own maven module - // to make clear that it cannot reference other classes from agent-internal? - - @OnMethodEnter - @SuppressWarnings("unchecked") - public static List before( - @This(optional = true) Object that, - @Origin Method method, - @AllArguments Object[] args - ) { - // that is null when instrumenting static methods. - Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); - try { - // The following code is equivalent to: - // return Delegator.before(that, method, args); - // However, the Delegator class will not be available in the context of the instrumented method, - // so we must use our agent class loader to load the Delegator class and do the call via reflection. - Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.agent.internal.Delegator"); - Method beforeMethod = delegator.getMethod("before", Class.class, Method.class, Object[].class); - return (List) beforeMethod.invoke(null, clazz, method, args); - } catch (Exception e) { - System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); - e.printStackTrace(); - return null; - } - } - - @OnMethodExit(onThrowable = Throwable.class) - public static void after( - @Enter List hooks, - @This(optional = true) Object that, - @Origin Method method, - @AllArguments Object[] args, - @Return(typing = Assigner.Typing.DYNAMIC) Object returned, // support void == null and int == Integer - @Thrown Throwable thrown - ) { - try { - // The following code is equivalent to: - // Delegator.after(hooks, method, args); - // However, the Delegator class will not be available in the context of the instrumented method, - // so we must use our agent class loader to load the Delegator class and do the call via reflection. - Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.agent.internal.Delegator"); - Method afterMethod = delegator.getMethod("after", List.class, Method.class, Object[].class, Object.class, Throwable.class); - afterMethod.invoke(null, hooks, method, args, returned, thrown); - } catch (Exception e) { - Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); - System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); - e.printStackTrace(); - } - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java deleted file mode 100644 index 0a89b53..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.agent.internal.jmx; - -import io.prometheus.client.CollectorRegistry; -import io.prometheus.client.exporter.common.TextFormat; - -import java.io.IOException; -import java.io.StringWriter; - -public class Exporter implements ExporterMBean { - - private final CollectorRegistry registry; - - public Exporter(CollectorRegistry registry) { - this.registry = registry; - } - - @Override - public String getTextFormat() { - try { - StringWriter result = new StringWriter(); - TextFormat.write004(result, registry.metricFamilySamples()); - return result.toString(); - } catch (IOException e) { - throw new RuntimeException("Unexpected error when writing metrics to a String: " + e.getMessage(), e); - } - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java deleted file mode 100644 index 1812b3d..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.agent.internal.jmx; - -public interface ExporterMBean { - String getTextFormat(); -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java deleted file mode 100644 index 30f4742..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java +++ /dev/null @@ -1,35 +0,0 @@ - - -package io.agent.internal.jmx; - -import io.prometheus.client.Collector; - -import java.util.HashMap; -import java.util.Map; - -public class Metric implements MetricMBean { - - private final Collector metric; - - Metric(Collector metric) { - this.metric = metric; - } - - /** - * @see MetricMBean#getValues() - */ - @Override - public Map, Double> getValues() { - Map, Double> result = new HashMap<>(); - for (Collector.MetricFamilySamples samples : metric.collect()) { - for (Collector.MetricFamilySamples.Sample sample : samples.samples) { - Map labels = new HashMap<>(); - for (int i = 0; i < sample.labelNames.size(); i++) { - labels.put(sample.labelNames.get(i), sample.labelValues.get(i)); - } - result.put(labels, sample.value); - } - } - return result; - } -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java deleted file mode 100644 index 151ca17..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java +++ /dev/null @@ -1,16 +0,0 @@ - - -package io.agent.internal.jmx; - -import java.util.Map; - -public interface MetricMBean { - - /** - * Get the values in a representation that can be used in MXBeans in JMX. - *

- * The result is a map of labels -> value. - * The labels themselves are represented as a key -> value map. - */ - Map, Double> getValues(); -} diff --git a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java b/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java deleted file mode 100644 index 89764cd..0000000 --- a/war&&jar_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.agent.internal.jmx; - -import io.prometheus.client.Collector; -import io.prometheus.client.CollectorRegistry; -import io.prometheus.client.SimpleCollector; - -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; -import java.lang.management.ManagementFactory; -import java.lang.reflect.Field; - -/** - * This is like the regular {@link CollectorRegistry}, except that when you {@link #register(Collector)} a metric, - * the metric will also be registered as an MBean in the JMX platform server. - */ -public class agentCollectorRegistry extends CollectorRegistry { - - @Override - public void register(Collector metric) { - super.register(metric); - try { - ManagementFactory.getPlatformMBeanServer().registerMBean(new Metric(metric), makeObjectName((SimpleCollector) metric)); - } catch (Exception e) { - throw new RuntimeException("Failed to register metric: " + e.getMessage(), e); - } - } - - private static ObjectName makeObjectName(SimpleCollector metric) throws MalformedObjectNameException { - return makeObjectName(getFullName(metric)); - } - - private static ObjectName makeObjectName(String fullname) throws MalformedObjectNameException { - return new ObjectName("io.agent:type=metrics,name=" + fullname); - } - - private static String getFullName(SimpleCollector metric) { - // Unfortunately, there is no public API to get the 'fullname' of a metric. We use reflection to get it anyway. - try { - Field field = SimpleCollector.class.getDeclaredField("fullname"); - field.setAccessible(true); - return (String) field.get(metric); - } catch (IllegalAccessException | NoSuchFieldException e) { - throw new RuntimeException("Failed to access " + metric.getClass().getName() + ".fullname. " + - "This is probably because the internal implementation of the client library has changed. " + - "You should adapt the agent accordingly.", e); - } - } - - public void registerNoJmx(Collector collector) { - super.register(collector); - } -} diff --git a/war&&jar_agent/agent-framework/agent-loader/pom.xml b/war&&jar_agent/agent-framework/agent-loader/pom.xml deleted file mode 100644 index d4a1455..0000000 --- a/war&&jar_agent/agent-framework/agent-loader/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-loader - agent-loader attach agent to a running JVM - - jar - - - - org.junit.jupiter - junit-jupiter-api - test - - - - com.sun - tools - 1.8 - system - ${java.home}/../lib/tools.jar - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - io.agent.loader.agentLoader - - - - - - - diff --git a/war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java b/war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java deleted file mode 100644 index e096f25..0000000 --- a/war&&jar_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.agent.loader; - -import com.sun.tools.attach.AgentLoadException; -import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; - -public class agentLoader { - - public static void main(String[] args) throws Exception { - int pid = getIntArg(args, "-pid"); - int port = getIntArg(args, "-port"); - String agentJar = getStringArg(args, "-agent"); - agentLoader.loadagent(agentJar, pid, port); - } - - private static void loadagent(String agentJar, int pid, int port) throws Exception { - VirtualMachineDescriptor vmd = findVirtualMachine(Integer.toString(pid)); - if (vmd == null) { - System.err.println("No Java process found with PID " + pid); - System.exit(-1); - } - VirtualMachine vm = null; - try { - vm = VirtualMachine.attach(vmd); - vm.loadAgent(agentJar, "port=" + port); - } catch (AgentLoadException e) { - System.err.println("Failed to attach agent: " + getMessage(e)); - } finally { - if (vm != null) { - vm.detach(); - } - } - } - - private static VirtualMachineDescriptor findVirtualMachine(String pid) { - for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { - if (vmd.id().equalsIgnoreCase(pid)) { - return vmd; - } - } - return null; - } - - private static String getMessage(AgentLoadException e) { - switch (e.getMessage()) { - case "-4": - return "Insuffient memory"; - case "100": - return "Agent JAR not found or no Agent-Class attribute"; - case "101": - return "Unable to add JAR file to system class path"; - case "102": - return "Agent JAR loaded but agent failed to initialize"; - default: - return e.getMessage(); - } - } - - private static int getIntArg(String[] args, String option) { - String stringArg = getStringArg(args, option); - try { - return Integer.parseInt(stringArg); - } catch (NumberFormatException e) { - System.err.println(option + " " + stringArg + ": invalid argument"); - System.exit(-1); - return 0; // will never happen - } - } - - private static String getStringArg(String[] args, String option) { - for (int pos : new int[]{0, 2, 4}) { - if (args.length < pos + 2) { - printUsageAndExit(); - } - if (option.equals(args[pos])) { - return args[pos+1]; - } - } - printUsageAndExit(); - return null; // will never happen - } - - private static void printUsageAndExit() { - System.err.println("Usage: java -cp $JAVA_HOME/lib/tools.jar:/path/to/agent-loader.jar io.agent.loader.agentLoader -agent /path/to/agent.jar -port 9300 -pid "); - System.exit(-1); - } -} diff --git a/war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml b/war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml deleted file mode 100644 index d626cc6..0000000 --- a/war&&jar_agent/agent-framework/agent-maven-plugin/pom.xml +++ /dev/null @@ -1,73 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-maven-plugin - agent-maven-plugin - - maven-plugin - - - - - io.agent - agent-internal - 1.0-SNAPSHOT - runtime - - - - io.agent - agent-agent - 1.0-SNAPSHOT - runtime - - - - io.agent - agent-api - 1.0-SNAPSHOT - runtime - - - - org.apache.maven - maven-plugin-api - 3.6.0 - provided - - - - org.apache.maven - maven-core - 3.6.0 - provided - - - - org.apache.maven.plugin-tools - maven-plugin-annotations - 3.6.0 - provided - - - - - - - - org.apache.maven.plugins - maven-plugin-plugin - 3.5 - - agent - - - - - diff --git a/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java b/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java deleted file mode 100644 index 780d370..0000000 --- a/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java +++ /dev/null @@ -1,165 +0,0 @@ - - -package io.agent.plugin; - -import org.apache.maven.artifact.Artifact; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.descriptor.PluginDescriptor; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; - -import static java.util.stream.Collectors.toList; - -/** - * Hard-coded list of JAR files that must go into the agent.jar. - * Versions are resolved through Maven. - * If the user adds a duplicate JAR with a different version, the plugin fails. - */ -class AgentDependencies { - - private final String pluginGroupId; - private final List dependencies; - - /** - * Hard-coded dependencies without versions (versions are resolved dynamically). - */ - private static class ExpectedDependency { - - final String groupId; - final String artifactId; - - private ExpectedDependency(String groupId, String artifactId) { - this.groupId = groupId; - this.artifactId = artifactId; - } - } - - private AgentDependencies(String pluginGroupId, List dependencies) { - this.pluginGroupId = pluginGroupId; - this.dependencies = dependencies; - } - - static AgentDependencies init(PluginDescriptor pluginDescriptor) throws MojoExecutionException { - - String pluginGroupId = pluginDescriptor.getGroupId(); - String pluginArtifactId = pluginDescriptor.getArtifactId(); - - List expectedDependencies = Arrays.asList( - new ExpectedDependency(pluginGroupId, "agent-agent"), - new ExpectedDependency(pluginGroupId, "agent-internal"), - new ExpectedDependency(pluginGroupId, "agent-api"), - new ExpectedDependency("io.prometheus", "simpleclient_common"), - new ExpectedDependency("io.prometheus", "simpleclient"), - new ExpectedDependency("net.bytebuddy", "byte-buddy"), - new ExpectedDependency("commons-io", "commons-io") - ); - - List actualDependencies = resolveVersions(pluginDescriptor, pluginArtifactId, expectedDependencies); - failUnlessComplete(actualDependencies, expectedDependencies, pluginArtifactId); - return new AgentDependencies(pluginGroupId, actualDependencies); - } - - /** - * Artifact for the agent-agent module. - */ - Artifact getAgentArtifact() { - return dependencies.stream() - .filter(isAgent()) - .findFirst() - .get(); // We know it's present. - } - - /** - * Artifact for all other runtime dependencies except agent-agent. - */ - List getDependencies() { - return dependencies.stream() - .filter(isAgent().negate()) - .collect(toList()); - } - - void assertNoConflict(Artifact artifact) throws MojoExecutionException { - Optional builtInVersion = dependencies.stream() - .filter(dependency -> dependency.getGroupId().equals(artifact.getGroupId())) - .filter(dependency -> dependency.getArtifactId().equals(artifact.getArtifactId())) - .map(Artifact::getVersion) - .findFirst(); - if (builtInVersion.isPresent() && ! builtInVersion.get().equals(artifact.getVersion())) { - String artifactName = artifact.getGroupId() + ":" + artifact.getArtifactId(); - throw new MojoExecutionException("Conflicting dependencies: Your project includes " + artifactName + - " version " + artifact.getVersion() + " but the agent-maven-plugin is built with version " + builtInVersion.get()); - } - } - - private static List resolveVersions(PluginDescriptor pluginDescriptor, String pluginArtifactId, List expectedDependencies) throws MojoExecutionException { - List actualDependencies = new ArrayList<>(); - for (Artifact artifact : pluginDescriptor.getArtifacts()) { - if (! isExpected(artifact, expectedDependencies)) { - continue; - } - if (isKnown(artifact, actualDependencies)) { - continue; - } - failOnVersionConflict(artifact, actualDependencies, pluginArtifactId); - actualDependencies.add(artifact); - } - return actualDependencies; - } - - private Predicate isAgent() { - return artifact -> artifact.getGroupId().equals(pluginGroupId) && artifact.getArtifactId().equals("agent-agent"); - } - - - private static Predicate expectedDependencyMatcher(Artifact artifact) { - return expectedDependency -> expectedDependency.groupId.equals(artifact.getGroupId()) && - expectedDependency.artifactId.equals(artifact.getArtifactId()); - } - - private static Predicate expectedDependencyMatcher(ExpectedDependency expectedDependency) { - return artifact -> expectedDependencyMatcher(artifact).test(expectedDependency); - } - - private static Predicate artifactMatcherWithoutVersion(Artifact artifact) { - return other -> artifact.getGroupId().equals(other.getGroupId()) && - artifact.getArtifactId().equals(other.getArtifactId()); - } - - private static Predicate artifactMatcherWithVersion(Artifact artifact) { - return other -> artifactMatcherWithoutVersion(artifact).test(other) && - artifact.getVersion().equals(other.getVersion()); - } - - private static boolean isExpected(Artifact artifact, List expectedDependencies) { - return expectedDependencies.stream().anyMatch(expectedDependencyMatcher(artifact)); - } - - private static boolean isKnown(Artifact artifact, List knownArtifacts) { - return knownArtifacts.stream().anyMatch(artifactMatcherWithVersion(artifact)); - } - - private static void failOnVersionConflict(Artifact artifact, List knownArtifacts, String pluginArtifactId) throws MojoExecutionException { - Optional conflictingVersion = knownArtifacts.stream() - .filter(artifactMatcherWithoutVersion(artifact)) - .filter(artifactMatcherWithVersion(artifact).negate()) // same version -> not conflicting - .findFirst() - .map(Artifact::getVersion); - if (conflictingVersion.isPresent()) { - String artifactName = artifact.getGroupId() + artifact.getArtifactId(); - throw new MojoExecutionException("version conflict in " + pluginArtifactId + ": " + artifactName + " found in version " + artifact.getVersion() + " and version " + conflictingVersion.get()); - } - } - - private static void failUnlessComplete(List actualDependencies, List expectedDependencies, String pluginArtifactId) throws MojoExecutionException { - for (ExpectedDependency expected : expectedDependencies) { - if (actualDependencies.stream().noneMatch(expectedDependencyMatcher(expected))) { - String dependencyName = expected.groupId + ":" + expected.artifactId; - throw new MojoExecutionException("Plugin dependency " + dependencyName + " missing. This is a bug in " + pluginArtifactId + "."); - } - } - } -} diff --git a/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java b/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java deleted file mode 100644 index eb9eca0..0000000 --- a/war&&jar_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java +++ /dev/null @@ -1,123 +0,0 @@ - - -package io.agent.plugin; - -import org.apache.maven.plugin.MojoExecutionException; -import org.codehaus.plexus.util.IOUtil; - -import java.io.*; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarOutputStream; - -class AgentJar implements AutoCloseable { - - private final String jarName; - private final JarOutputStream jarOutputStream; - private final Set content = new HashSet<>(); - - enum Directory { - SHARED_JARS("shared-jars/"), // Directory entries in JAR file must end in '/' - PER_DEPLOYMENT_JARS("per-deployment-jars/"); - - private final String name; - - Directory(String name) { - this.name = name; - } - - public String getName() { - return name; - } - } - - private AgentJar(String jarName, JarOutputStream jarOutputStream) { - this.jarName = jarName; - this.jarOutputStream = jarOutputStream; - } - - static AgentJar create(File jarFile) throws MojoExecutionException { - try { - JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile))); - return new AgentJar(jarFile.getName(), jarOutputStream); - } catch (IOException e) { - throw new MojoExecutionException("Error creating " + jarFile.getName() + ": " + e.getMessage(), e); - } - } - - void addFile(File srcFile, String targetFileName, Directory targetDir) throws MojoExecutionException { - String destPath = targetDir.getName() + targetFileName; - if (content.contains(destPath)) { - return; - } - makeDirsRecursively(destPath); - content.add(destPath); - try (InputStream in = new FileInputStream(srcFile)) { - jarOutputStream.putNextEntry(new JarEntry(destPath)); - IOUtil.copy(in, jarOutputStream); - } catch (IOException e) { - throw new MojoExecutionException("Error adding " + srcFile.getName() + " to target JAR: " + e.getMessage(), e); - } - } - - void addFile(File srcFile, Directory targetDir) throws MojoExecutionException { - addFile(srcFile, srcFile.getName(), targetDir); - } - - void extractJar(File jar, ManifestTransformer manifestTransformer) throws MojoExecutionException { - try (JarFile jarFile = new JarFile(jar)) { - for (Enumeration jarEntries = jarFile.entries(); jarEntries.hasMoreElements(); ) { - JarEntry jarEntry = jarEntries.nextElement(); - if (manifestTransformer.canTransform(jarEntry)) { - jarEntry = manifestTransformer.transform(jarEntry); - } - if (!jarEntry.isDirectory() && !content.contains(jarEntry.getName())) { - content.add(jarEntry.getName()); - makeDirsRecursively(jarEntry.getName()); - try (InputStream in = getInputStream(jarEntry, jarFile, manifestTransformer)) { - jarOutputStream.putNextEntry(jarEntry); - IOUtil.copy(in, jarOutputStream); - } - } - } - } catch (IOException e) { - throw new MojoExecutionException("Error adding " + jar.getName() + " to target JAR: " + e.getMessage(), e); - } - } - - private InputStream getInputStream(JarEntry jarEntry, JarFile jarFile, ManifestTransformer manifestTransformer) throws IOException, MojoExecutionException { - InputStream in = jarFile.getInputStream(jarEntry); - if (manifestTransformer.canTransform(jarEntry)) { - in = manifestTransformer.transform(in); - } - return in; - } - - private void makeDirsRecursively(String path) throws MojoExecutionException { - String[] parts = path.split("/+"); - String segment = ""; - for (int i=0; i - 4.0.0 - - io.agent - agent-framework - 1.0-SNAPSHOT - - pom - agent framework - - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - UTF-8 - UTF-8 - 1.8 - 1.8 - 5.3.1 - - - - - - agent-loader - - agent-agent - agent-api - agent-internal - agent-exporter - agent-loader - agent-maven-plugin - - - - - default - - true - - - agent-agent - agent-api - agent-internal - agent-exporter - agent-maven-plugin - - - - - - - - - io.prometheus - simpleclient_common - 0.5.0 - - - org.junit.jupiter - junit-jupiter-api - ${junit5.version} - test - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - 2.22.1 - - - - - diff --git a/war&&jar_agent/war_agent/pom-with-docker-tests.xml b/war&&jar_agent/war_agent/pom-with-docker-tests.xml deleted file mode 100644 index 94d727f..0000000 --- a/war&&jar_agent/war_agent/pom-with-docker-tests.xml +++ /dev/null @@ -1,386 +0,0 @@ - - 4.0.0 - - io.agent - agent-example - 1.0-SNAPSHOT - - agent example - - jar - - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.0-SNAPSHOT - - - - - - io.agent - agent-api - ${agent.framework.version} - provided - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - com.squareup.okhttp3 - okhttp - 3.11.0 - test - - - - - - agent - - - - org.apache.maven.plugins - maven-failsafe-plugin - 2.22.1 - - - - - - io.agent - agent-maven-plugin - ${agent.framework.version} - - - agent - package - - build - - - - - - - - io.fabric8 - docker-maven-plugin - 0.27.2 - - true - - - - - ${project.artifactId}/oracle-jdk-8 - - ubuntu:17.10 - /opt - - apt-get update ; apt-get upgrade -y - apt-get install -y curl git neovim sudo - curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz - tar xfz jdk-8u192-linux-x64.tar.gz - curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz - tar xfz apache-maven-3.6.0-bin.tar.gz - - echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ - echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ - echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ - echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; - - adduser --disabled-password --gecos '' agent - echo 'agent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers - - agent - - /opt/jdk1.8.0_192 - /opt/apache-maven-3.6.0 - /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH - - - - - - - ${project.artifactId}/wildfly-kitchensink - - ${project.artifactId}/oracle-jdk-8 - /home/agent - - curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz - tar xfz wildfly-10.1.0.Final.tar.gz - rm wildfly-10.1.0.Final.tar.gz - git clone https://github.com/wildfly/quickstart.git - cd quickstart; git checkout 10.x - cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package - mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments - - mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments - rm -rf ./quickstart ; rm -rf ./.m2 - - - - - - - ${project.artifactId}/wildfly-kitchensink-agent - - ${project.artifactId}/wildfly-kitchensink - - - echo '#!/bin/bash' >> run.sh ; \ - echo >> run.sh ; \ - echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ - echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ - echo 'export JAVA_OPTS="' >> run.sh ; \ - echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ - echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ - echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent' >> run.sh ; \ - echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ - echo ' -javaagent:../agent.jar=port=9300' >> run.sh ; \ - echo '"' >> run.sh ; \ - echo >> run.sh ; \ - echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ - chmod 755 run.sh ; - - - - /home/agent - agent:agent:agent - - - - target/agent.jar - / - - - - - - - - - - ${wildfly.port}:8080 - ${agent.port}:9300 - - - WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms - - - ./run.sh - - true - [Wildfly] - - - - - - - ${project.artifactId}/spring - - ${project.artifactId}/oracle-jdk-8 - /home/agent - - git clone https://github.com/spring-guides/gs-accessing-data-rest.git - cd gs-accessing-data-rest/complete; mvn --batch-mode package - mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . - rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 - - - - - - - ${project.artifactId}/spring-agent - - ${project.artifactId}/spring - - - echo '#!/bin/bash' >> run.sh ; \ - echo >> run.sh ; \ - echo 'java -javaagent:agent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ - chmod 755 run.sh ; - - - - /home/agent - agent:agent:agent - - - - target/agent.jar - / - - - - - - - - - - ${spring.port}:8080 - ${agent.port}:9300 - - - Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) - - - ./run.sh - - true - [Spring Boot] - - - - - - - - - start - pre-integration-test - - build - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - - none - - skip-docker-tests - - - - wildfly - - - ${project.artifactId}/oracle-jdk-8, - ${project.artifactId}/wildfly-kitchensink, - ${project.artifactId}/wildfly-kitchensink-agent - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/WildflyIT.java - - - - - http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink - http://${docker.host.address}:${agent.port} - - - - - integration-tests - - integration-test - verify - - - - - - - - - spring - - - ${project.artifactId}/oracle-jdk-8, - ${project.artifactId}/spring, - ${project.artifactId}/spring-agent - - - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - **/SpringIT.java - - - - - http://${docker.host.address}:${spring.port} - http://${docker.host.address}:${agent.port} - - - - - integration-tests - - integration-test - verify - - - - - - - - - diff --git a/war&&jar_agent/war_agent/pom.xml b/war&&jar_agent/war_agent/pom.xml deleted file mode 100644 index 3686b0b..0000000 --- a/war&&jar_agent/war_agent/pom.xml +++ /dev/null @@ -1,76 +0,0 @@ - - 4.0.0 - - io.agent - agent-example - 1.0-SNAPSHOT - - agent example - - jar - - - UTF-8 - UTF-8 - UTF-8 - 1.8 - 1.8 - 1.0-SNAPSHOT - - - - - - io.agent - agent-api - ${agent.framework.version} - provided - - - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - com.squareup.okhttp3 - okhttp - 3.11.0 - test - - - - - - agent - - - io.agent - agent-maven-plugin - ${agent.framework.version} - - - agent - package - - build - - - - - - - - diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java b/war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java deleted file mode 100644 index 15f12e8..0000000 --- a/war&&jar_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java +++ /dev/null @@ -1,36 +0,0 @@ - - -package io.agent.collectors; - -import com.sun.management.OperatingSystemMXBean; -import io.prometheus.client.Collector; - -import java.lang.management.ManagementFactory; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) -// TODO -- This class is not loaded by default, see commented-out lines in io.agent.internal.agent.premain() -// See JmxCollector in jmx_exporter -public class JmxCollector extends Collector implements Collector.Describable { - - @Override - public List collect() { - List result = new ArrayList<>(); - result.add(collectOperatingSystemMetrics()); - return Collections.unmodifiableList(result); - } - - @Override - public List describe() { - return new ArrayList<>(); - } - - private MetricFamilySamples collectOperatingSystemMetrics() { - OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); - MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); - return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); - } -} diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java deleted file mode 100644 index a0c5168..0000000 --- a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.agent.hooks; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -/** - * Store the HTTP context in a thread-local, so that we know which database call was triggered by which REST service. - */ -class HttpContext { - - static class Key {} - - static final Key HTTP_METHOD = new Key<>(); - static final Key HTTP_PATH = new Key<>(); - - private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); - - static void put(Key key, T value) { - threadLocal.get().put(key, value); - } - - @SuppressWarnings("unchecked") - static Optional get(Key key) { - return Optional.ofNullable((T) threadLocal.get().get(key)); - } - - static void clear(Key... keys) { - for (Key key : keys) { - threadLocal.get().remove(key); - } - } -} diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java deleted file mode 100644 index 05255ca..0000000 --- a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java +++ /dev/null @@ -1,129 +0,0 @@ - - -package io.agent.hooks; - -import io.agent.annotations.After; -import io.agent.annotations.Before; -import io.agent.annotations.Hook; -import io.agent.hookcontext.MetricDef; -import io.agent.hookcontext.MetricsStore; -import io.prometheus.client.Counter; -import io.prometheus.client.Summary; - -import java.util.concurrent.TimeUnit; - -import static io.agent.hooks.HttpContext.HTTP_METHOD; -import static io.agent.hooks.HttpContext.HTTP_PATH; - -@Hook(instruments = { - "java.sql.Statement", - "java.sql.Connection" -}) -public class JdbcHook { - - private final Counter sqlQueriesTotal; - private final Summary sqlQueriesDuration; - private long startTime = 0; - - public JdbcHook(MetricsStore metricsStore) { - - sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( - "sql_queries_total", - (name, registry) -> Counter.build() - .name(name) - .labelNames("method", "path", "query") - .help("Total number of sql queries.") - .register(registry) - )); - - sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( - "sql_query_duration", - (name, registry) -> Summary.build() - .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error - .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error - .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error - .name(name) - .labelNames("method", "path", "query") - .help("Duration for serving the sql queries in seconds.") - .register(registry) - )); - } - - private String stripValues(String query) { - // We want the structure of the query as labels, not the actual values. - // Therefore, we replace: - // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') - // with - // insert into Member (id, name, email, phone_number) values (...) - return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); - } - - // --- before - - @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) - public void before(String sql) { - startTime = System.nanoTime(); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, int autoGeneratedKeys) { - before(sql); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, int[] columnIndexes) { - before(sql); - } - - @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void before(String sql, String[] columnNames) { - before(sql); - } - - @Before(method = {"prepareStatement", "prepareCall"}) - public void before(String sql, int resultSetType, int resultSetConcurrency) { - before(sql); - } - - @Before(method = {"prepareStatement", "prepareCall"}) - public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { - before(sql); - } - - // --- after - - @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) - public void after(String sql) throws Exception { - double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); - String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); - String path = HttpContext.get(HTTP_PATH).orElse("no http context"); - String query = stripValues(sql); - sqlQueriesTotal.labels(method, path, query).inc(); - sqlQueriesDuration.labels(method, path, query).observe(duration); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, int autoGeneratedKeys) throws Exception { - after(sql); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, int[] columnIndexes) throws Exception { - after(sql); - } - - @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) - public void after(String sql, String[] columnNames) throws Exception { - after(sql); - } - - @After(method = {"prepareStatement", "prepareCall"}) - public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { - after(sql); - } - - @After(method = {"prepareStatement", "prepareCall"}) - public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { - after(sql); - } -} diff --git a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java b/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java deleted file mode 100644 index 70bcf4d..0000000 --- a/war&&jar_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java +++ /dev/null @@ -1,108 +0,0 @@ - - -package io.agent.hooks; - -import io.agent.annotations.After; -import io.agent.annotations.Before; -import io.agent.annotations.Hook; -import io.agent.hookcontext.MetricDef; -import io.agent.hookcontext.MetricsStore; -import io.prometheus.client.Counter; -import io.prometheus.client.Summary; - -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.concurrent.TimeUnit; - -import static io.agent.hooks.HttpContext.HTTP_METHOD; -import static io.agent.hooks.HttpContext.HTTP_PATH; - -@Hook(instruments = { - "javax.servlet.Servlet", - "javax.servlet.Filter" -}) -public class ServletHook { - - private final Counter httpRequestsTotal; - private final Summary httpRequestsDuration; - private long startTime = 0; - - public ServletHook(MetricsStore metricsStore) { - - httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( - "http_requests_total", - (name, registry) -> Counter.build() - .name(name) - .labelNames("method", "path", "status") - .help("Total number of http requests.") - .register(registry) - )); - - httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( - "http_request_duration", - (name, registry) -> Summary.build() - .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error - .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error - .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error - .name(name) - .labelNames("method", "path", "status") - .help("Duration for serving the http requests in seconds.") - .register(registry) - )); - } - - private String stripPathParameters(String path) { - - // The URL path may include path parameters. - // For example, REST URLs for querying an item might look like this: - // - // /item/1 - // /item/2 - // /item/3 - // etc. - // - // We don't want to create a new Prometheus label for each of these paths. - // Rather, we want a single label like this: - // - // /item/{id} - // - // This method replaces path parameters with placeholders. It is application specific and - // should be adapted depending on the actual paths in an application. - // For the demo, we just replace all numbers with {id}. - - return path - .replaceAll("/[0-9]+", "/{id}") - .replaceAll("/;jsessionid=\\w*", "") - .replaceAll("/$", "") - .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." - } - - @Before(method = {"service", "doFilter"}) - public void before(ServletRequest request, ServletResponse response) { - if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { - HttpServletRequest req = (HttpServletRequest) request; - HttpContext.put(HTTP_METHOD, req.getMethod()); - HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); - startTime = System.nanoTime(); - } - } - - // Return Werte und Exceptions als Parameter - @After(method = {"service", "doFilter"}) - public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { - if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { - HttpServletResponse resp = (HttpServletResponse) response; - try { - double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); - String method = HttpContext.get(HTTP_METHOD).get(); - String path = HttpContext.get(HTTP_PATH).get(); - httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); - httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); - } finally { - HttpContext.clear(HTTP_METHOD, HTTP_PATH); - } - } - } -} diff --git a/war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java b/war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java deleted file mode 100644 index d40844b..0000000 --- a/war&&jar_agent/war_agent/src/test/java/io/agent/it/SpringIT.java +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2017 The agent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.agent.it; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.junit.jupiter.api.Test; - -public class SpringIT { - - /** - * Run some HTTP queries against a Docker container from image agent/spring-agent. - *

- * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. - */ - @Test - public void testSpring() throws Exception { - OkHttpClient client = new OkHttpClient(); - Request metricsRequest = new Request.Builder().url(System.getProperty("agent.url") + "/metrics").build(); - - // Execute two POST requests - Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); - assertEquals(201, restResponse.code()); - restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); - assertEquals(201, restResponse.code()); - - // Query Prometheus metrics from agent - Response metricsResponse = client.newCall(metricsRequest).execute(); - String[] metricsLines = metricsResponse.body().string().split("\n"); - - String httpRequestsTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("http_requests_total")) - .filter(m -> m.contains("method=\"POST\"")) - .filter(m -> m.contains("path=\"/people\"")) - .filter(m -> m.contains("status=\"201\"")) - .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); - - assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); - - String sqlQueriesTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("sql_queries_total")) - // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: - // query="insert into person (first_name, last_name, id)" - .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) - .filter(m -> m.contains("method=\"POST\"")) - .filter(m -> m.contains("path=\"/people\"")) - .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); - - assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); - } - - private Request makePostRequest(String firstName, String lastName) { - return new Request.Builder() - .url(System.getProperty("deployment.url") + "/people") - .method("POST", RequestBody.create( - MediaType.parse("application/json"), - "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) - .build(); - } -} diff --git a/war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java b/war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java deleted file mode 100644 index a6132ba..0000000 --- a/war&&jar_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2017 The agent Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package io.agent.it; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class WildflyIT { - - /** - * Run some HTTP queries against a Docker container from image agent/wildfly-kitchensink-agent. - *

- * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. - */ - @Test - public void testWildfly() throws Exception { - OkHttpClient client = new OkHttpClient(); - Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); - - // Execute REST call - Response restResponse = client.newCall(restRequest).execute(); - Assertions.assertEquals(restResponse.code(), 200); - Assertions.assertTrue(restResponse.body().string().contains("John Smith")); - - Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric - assertMetrics(client, "1.0"); - - // Execute REST call again - restResponse = client.newCall(restRequest).execute(); - Assertions.assertEquals(restResponse.code(), 200); - Assertions.assertTrue(restResponse.body().string().contains("John Smith")); - - Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric - assertMetrics(client, "2.0"); - } - - private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { - - Request metricsRequest = new Request.Builder().url(System.getProperty("agent.url") + "/metrics").build(); - Response metricsResponse = client.newCall(metricsRequest).execute(); - String[] metricsLines = metricsResponse.body().string().split("\n"); - - String httpRequestsTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("http_requests_total")) - .filter(m -> m.contains("method=\"GET\"")) - .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) - .filter(m -> m.contains("status=\"200\"")) - .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); - - assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); - - String sqlQueriesTotal = Arrays.stream(metricsLines) - .filter(m -> m.contains("sql_queries_total")) - .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) - .filter(m -> m.contains("method=\"GET\"")) - .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) - .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); - - assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); - } -} From 4119846e89573b32915ead99a7777ac615daed47 Mon Sep 17 00:00:00 2001 From: anu Date: Mon, 18 Feb 2019 00:39:26 +0530 Subject: [PATCH 29/38] . --- .../java/io/agent/agent/JarFilesTest.java | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java diff --git a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java b/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java deleted file mode 100644 index 4b35a71..0000000 --- a/war&&jar_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.agent.agent; - -import org.junit.jupiter.api.Test; - -import static io.agent.agent.JarFiles.findAgentJarFromCmdline; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.*; - -class JarFilesTest { - - @Test - void testCmdlineParserWildfly() { - // The command line arguments are taken from the Wildfly application server example. - String[] cmdlineArgs = new String[]{ - "-D[Standalone]", - "-Xbootclasspath/p:/tmp/wildfly-10.1.0.Final/modules/system/layers/base/org/jboss/logmanager/main/jboss-logmanager-2.0.4.Final.jar", - "-Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent", - "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", - "-javaagent:../agent/agent-dist/target/agent.jar=port=9300", - "-Dorg.jboss.boot.log.file=/tmp/wildfly-10.1.0.Final/standalone/log/server.log", - "-Dlogging.configuration=file:/tmp/wildfly-10.1.0.Final/standalone/configuration/logging.properties" - }; - assertEquals("../agent/agent-dist/target/agent.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); - } - - @Test - void testCmdlineParserVersioned() { - String[] cmdlineArgs = new String[] { - "-javaagent:agent-1.0-SNAPSHOT.jar" - }; - assertEquals("agent-1.0-SNAPSHOT.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); - } - - @Test() - void testCmdlineParserFailed() { - String[] cmdlineArgs = new String[] { - "-javaagent:/some/other/agent.jar", - "-jar", - "agent.jar" - }; - Exception e = assertThrows(Exception.class, () -> findAgentJarFromCmdline(asList(cmdlineArgs))); - // The exception should contain some message indicating agent.jar was not found. - assertTrue(e.getMessage().contains("agent.jar")); - } -} From d4bbc17bf9d7e900eb64158ece3519635a7d32ec Mon Sep 17 00:00:00 2001 From: Shri Date: Mon, 18 Feb 2019 09:23:42 +0530 Subject: [PATCH 30/38] Deleted unwanted documents' --- dataCollectorFetcher | 1 - docker-agent | 1 - 2 files changed, 2 deletions(-) delete mode 160000 dataCollectorFetcher delete mode 160000 docker-agent diff --git a/dataCollectorFetcher b/dataCollectorFetcher deleted file mode 160000 index b3abb4b..0000000 --- a/dataCollectorFetcher +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b3abb4b465df5a8775fc34153327526e2454ff6e diff --git a/docker-agent b/docker-agent deleted file mode 160000 index 8dc9925..0000000 --- a/docker-agent +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8dc99254129437a5d4410e535691916f93228359 From 4ce6cfd0ec047a31379aeaf526856080206f207d Mon Sep 17 00:00:00 2001 From: anu Date: Mon, 18 Feb 2019 09:27:38 +0530 Subject: [PATCH 31/38] ADDED agent code --- .../agent-framework/agent-agent/pom.xml | 42 ++ .../java/io/agent/agent/ClassLoaderCache.java | 65 +++ .../main/java/io/agent/agent/JarFiles.java | 127 ++++++ .../agent/agent/PerDeploymentClassLoader.java | 43 ++ .../src/main/java/io/agent/agent/agent.java | 20 + .../java/io/agent/agent/JarFilesTest.java | 45 ++ java_agent/agent-framework/agent-api/pom.xml | 21 + .../main/java/io/agent/annotations/After.java | 17 + .../java/io/agent/annotations/Before.java | 17 + .../main/java/io/agent/annotations/Hook.java | 29 ++ .../java/io/agent/annotations/Returned.java | 18 + .../java/io/agent/annotations/Thrown.java | 33 ++ .../java/io/agent/hookcontext/MetricDef.java | 38 ++ .../io/agent/hookcontext/MetricsStore.java | 38 ++ .../agent-framework/agent-exporter/pom.xml | 31 ++ .../agent/exporter/agentExporterServlet.java | 29 ++ .../agent-framework/agent-internal/pom.xml | 69 ++++ .../java/io/agent/internal/BuiltInServer.java | 57 +++ .../java/io/agent/internal/Delegator.java | 264 ++++++++++++ .../java/io/agent/internal/HookException.java | 12 + .../java/io/agent/internal/HookInstance.java | 20 + .../java/io/agent/internal/HookMetadata.java | 128 ++++++ .../io/agent/internal/HookMetadataParser.java | 321 +++++++++++++++ .../main/java/io/agent/internal/agent.java | 141 +++++++ .../java/io/agent/internal/agentAdvice.java | 64 +++ .../java/io/agent/internal/jmx/Exporter.java | 27 ++ .../io/agent/internal/jmx/ExporterMBean.java | 5 + .../java/io/agent/internal/jmx/Metric.java | 35 ++ .../io/agent/internal/jmx/MetricMBean.java | 16 + .../internal/jmx/agentCollectorRegistry.java | 52 +++ .../agent-framework/agent-loader/pom.xml | 47 +++ .../java/io/agent/loader/agentLoader.java | 87 ++++ .../agent-maven-plugin/pom.xml | 73 ++++ .../io/agent/plugin/AgentDependencies.java | 163 ++++++++ .../main/java/io/agent/plugin/AgentJar.java | 121 ++++++ .../java/io/agent/plugin/JarFileNames.java | 63 +++ .../io/agent/plugin/ManifestTransformer.java | 51 +++ .../main/java/io/agent/plugin/agentMojo.java | 50 +++ java_agent/agent-framework/pom.xml | 85 ++++ .../war_agent/pom-with-docker-tests.xml | 386 ++++++++++++++++++ java_agent/war_agent/pom.xml | 76 ++++ .../io/agent/collectors/JmxCollector.java | 34 ++ .../main/java/io/agent/hooks/HttpContext.java | 33 ++ .../main/java/io/agent/hooks/JdbcHook.java | 127 ++++++ .../main/java/io/agent/hooks/ServletHook.java | 106 +++++ .../src/test/java/io/agent/it/SpringIT.java | 66 +++ .../src/test/java/io/agent/it/WildflyIT.java | 66 +++ 47 files changed, 3458 insertions(+) create mode 100644 java_agent/agent-framework/agent-agent/pom.xml create mode 100644 java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java create mode 100644 java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/JarFiles.java create mode 100644 java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java create mode 100644 java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java create mode 100644 java_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java create mode 100644 java_agent/agent-framework/agent-api/pom.xml create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java create mode 100644 java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java create mode 100644 java_agent/agent-framework/agent-exporter/pom.xml create mode 100644 java_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java create mode 100644 java_agent/agent-framework/agent-internal/pom.xml create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java create mode 100644 java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java create mode 100644 java_agent/agent-framework/agent-loader/pom.xml create mode 100644 java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java create mode 100644 java_agent/agent-framework/agent-maven-plugin/pom.xml create mode 100644 java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java create mode 100644 java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java create mode 100644 java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/JarFileNames.java create mode 100644 java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/ManifestTransformer.java create mode 100644 java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/agentMojo.java create mode 100644 java_agent/agent-framework/pom.xml create mode 100644 java_agent/war_agent/pom-with-docker-tests.xml create mode 100644 java_agent/war_agent/pom.xml create mode 100644 java_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java create mode 100644 java_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java create mode 100644 java_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java create mode 100644 java_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java create mode 100644 java_agent/war_agent/src/test/java/io/agent/it/SpringIT.java create mode 100644 java_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java diff --git a/java_agent/agent-framework/agent-agent/pom.xml b/java_agent/agent-framework/agent-agent/pom.xml new file mode 100644 index 0000000..97d4714 --- /dev/null +++ b/java_agent/agent-framework/agent-agent/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-agent + agent-agent loaded from the system class loader + + jar + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + io.agent.agent.agent + io.agent.agent.agent + true + true + true + + + + + + + diff --git a/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java b/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java new file mode 100644 index 0000000..70a70b7 --- /dev/null +++ b/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/ClassLoaderCache.java @@ -0,0 +1,65 @@ +package io.agent.agent; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * ClassLoaderCache stores the class loaders used for loading the agent modules, + * i.e. agent-hooks, agent-annotations, agent-internals, and their dependencies. + *

+ * For the Hooks (like ServletHook or JdbcHook) there is one class loader per deployment, + * because hook classes may reference classes from the deployment, + * e.g. as parameters to the before() and after() methods. + * All other modules and their dependencies are loaded through a shared class loader. + *

+ */ +public class ClassLoaderCache { + + private static ClassLoaderCache instance; + + // TODO: The cache does not free class loaders when applications are undeployed. Maybe use WeakHashMap? + private final Map cache = new HashMap<>(); + private final URLClassLoader sharedClassLoader; // shared across multiple deployments + private final List perDeploymentJars; // one class loader for each deployment for these JARs + + private ClassLoaderCache(JarFiles jarFiles) { + sharedClassLoader = new URLClassLoader(pathsToURLs(jarFiles.getSharedJars())); + perDeploymentJars = jarFiles.getPerDeploymentJars(); + } + + public static synchronized ClassLoaderCache getInstance() { + if (instance == null) { + instance = new ClassLoaderCache(JarFiles.extract()); + } + return instance; + } + + public List getPerDeploymentJars() { + return perDeploymentJars; + } + + public synchronized ClassLoader currentClassLoader() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (! cache.containsKey(contextClassLoader)) { + cache.put(contextClassLoader, new PerDeploymentClassLoader(pathsToURLs(perDeploymentJars), sharedClassLoader, contextClassLoader)); + } + return cache.get(contextClassLoader); + } + + private static URL[] pathsToURLs(List paths) { + try { + URL[] result = new URL[paths.size()]; + for (int i=0; i perDeploymentJars; // classes loaded for each deployment + private final List sharedJars; // classes shared across multiple deployments + + private JarFiles(List perDeploymentJars, List sharedJars) { + this.perDeploymentJars = Collections.unmodifiableList(perDeploymentJars); + this.sharedJars = Collections.unmodifiableList(sharedJars); + } + + List getPerDeploymentJars() { + return perDeploymentJars; + } + + List getSharedJars() { + return sharedJars; + } + + /** + * Theoretically we could return a list of jar:/ URLs without extracting the JARs, + * but the URLClassLoader has a bug such that jar:/ URLs cannot be used. Therefore, we have + * to extract the JARs and return a list of file:/ URLs. + * See https://bugs.openjdk.java.net/browse/JDK-4735639 + */ + static JarFiles extract() { + List perDeploymentJars = new ArrayList<>(); + List sharedJars = new ArrayList<>(); + Path agentJar = findAgentJar(); + List extractedJars; + try { + Path tmpDir = Files.createTempDirectory("agent-"); + tmpDir.toFile().deleteOnExit(); + extractedJars = unzip(agentJar, tmpDir, entry -> entry.getName().endsWith(".jar")); + } catch (IOException e) { + throw new RuntimeException("Failed to load agent.jar: " + e.getMessage(), e); + } + for (Path jar : extractedJars) { + if (jar.getParent().getFileName().toString().equals("per-deployment-jars")) { + perDeploymentJars.add(jar); + } else { + sharedJars.add(jar); + } + } + return new JarFiles(perDeploymentJars, sharedJars); + } + + private static List unzip(Path jarFile, Path destDir, Predicate filter) throws IOException { + List result = new ArrayList<>(); + try (JarFile agentJar = new JarFile(jarFile.toFile())) { + Enumeration jarEntries = agentJar.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry jarEntry = jarEntries.nextElement(); + if (filter.test(jarEntry)) { + Path destFile = destDir.resolve(jarEntry.getName()); + if (!destFile.getParent().toFile().exists()) { + if (!destFile.getParent().toFile().mkdirs()) { + throw new IOException("Failed to make directory: " + destFile.getParent()); + } + } + Files.copy(agentJar.getInputStream(jarEntry), destFile); + result.add(destFile); + } + } + } + return result; + } + + private static Path findAgentJar() { + CodeSource cs = agent.class.getProtectionDomain().getCodeSource(); + if (cs != null) { + return findAgentJarFromCodeSource(cs); + } else { + // This happens if the agent class is loaded from the bootstrap class loader, + // i.e. in addition to the command line argument -javaagent:/path/to/agent.jar, + // the argument -Xbootclasspath/p:/path/to/agent.jar is used. + return findAgentJarFromCmdline(ManagementFactory.getRuntimeMXBean().getInputArguments()); + } + } + + private static Path findAgentJarFromCodeSource(CodeSource cs) { + try { + return Paths.get(cs.getLocation().toURI()); + } catch (URISyntaxException e) { + throw new RuntimeException("Failed to load agent.jar from " + cs.getLocation() + ": " + e.getMessage(), e); + } + } + + static Path findAgentJarFromCmdline(List cmdlineArgs) { + Pattern p = Pattern.compile("^-javaagent:(.*agent([^/]*).jar)(=.*)?$"); + for (String arg : cmdlineArgs) { + Matcher m = p.matcher(arg); + if (m.matches()) { + return Paths.get(m.group(1)); + } + } + throw new RuntimeException("Failed to locate agent.jar file."); + } +} diff --git a/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java b/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java new file mode 100644 index 0000000..7f1ae8f --- /dev/null +++ b/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/PerDeploymentClassLoader.java @@ -0,0 +1,43 @@ +package io.agent.agent; + +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Class loader for loading Hooks (like ServletHook or JdbcHook). + *

+ * There is one instance per deployment in an application server, because hook classes may + * reference classes from the deployment, e.g. as parameters to the before() and after() methods. + *

+ * However, loading shared classes like the Prometheus client library is delegated to the {@link #sharedClassLoader}, + * because the Prometheus metric registry should be accessible across all deployments within an application server. + */ +class PerDeploymentClassLoader extends URLClassLoader { + + private final URLClassLoader sharedClassLoader; // for loading the Prometheus client library + + PerDeploymentClassLoader(URL[] perDeploymentJars, URLClassLoader sharedClassLoader, ClassLoader parent) { + super(perDeploymentJars, parent); + this.sharedClassLoader = sharedClassLoader; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + try { + return sharedClassLoader.loadClass(name); // The Prometheus client library should all have the same initiating loader across deployments. + } catch (ClassNotFoundException e) { + return super.loadClass(name); // Hooks should have different initiating loaders if the context loader differs. + } + } + + // Called by Byte buddy to load the agentAdvice. + @Override + public InputStream getResourceAsStream(String name) { + InputStream result = sharedClassLoader.getResourceAsStream(name); + if (result == null) { + result = super.getResourceAsStream(name); + } + return result; + } +} diff --git a/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java b/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java new file mode 100644 index 0000000..f687262 --- /dev/null +++ b/java_agent/agent-framework/agent-agent/src/main/java/io/agent/agent/agent.java @@ -0,0 +1,20 @@ +package io.agent.agent; + +import java.lang.instrument.Instrumentation; + +/** + * We want as little dependencies as possible on the system class loader, + * so the actual agent is loaded in its own class loader and this agent delegates to it. + */ +public class agent { + + public static void premain(String agentArgs, Instrumentation inst) throws Exception { + ClassLoader agentClassLoader = ClassLoaderCache.getInstance().currentClassLoader(); + Class agentClass = agentClassLoader.loadClass("io.agent.internal.agent"); + agentClass.getMethod("premain", String.class, Instrumentation.class).invoke(null, agentArgs, inst); + } + + public static void agentmain(String agentArgs, Instrumentation inst) throws Exception { + premain(agentArgs, inst); + } +} diff --git a/java_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java b/java_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java new file mode 100644 index 0000000..4b35a71 --- /dev/null +++ b/java_agent/agent-framework/agent-agent/src/test/java/io/agent/agent/JarFilesTest.java @@ -0,0 +1,45 @@ +package io.agent.agent; + +import org.junit.jupiter.api.Test; + +import static io.agent.agent.JarFiles.findAgentJarFromCmdline; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.*; + +class JarFilesTest { + + @Test + void testCmdlineParserWildfly() { + // The command line arguments are taken from the Wildfly application server example. + String[] cmdlineArgs = new String[]{ + "-D[Standalone]", + "-Xbootclasspath/p:/tmp/wildfly-10.1.0.Final/modules/system/layers/base/org/jboss/logmanager/main/jboss-logmanager-2.0.4.Final.jar", + "-Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent", + "-Djava.util.logging.manager=org.jboss.logmanager.LogManager", + "-javaagent:../agent/agent-dist/target/agent.jar=port=9300", + "-Dorg.jboss.boot.log.file=/tmp/wildfly-10.1.0.Final/standalone/log/server.log", + "-Dlogging.configuration=file:/tmp/wildfly-10.1.0.Final/standalone/configuration/logging.properties" + }; + assertEquals("../agent/agent-dist/target/agent.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); + } + + @Test + void testCmdlineParserVersioned() { + String[] cmdlineArgs = new String[] { + "-javaagent:agent-1.0-SNAPSHOT.jar" + }; + assertEquals("agent-1.0-SNAPSHOT.jar", findAgentJarFromCmdline(asList(cmdlineArgs)).toString()); + } + + @Test() + void testCmdlineParserFailed() { + String[] cmdlineArgs = new String[] { + "-javaagent:/some/other/agent.jar", + "-jar", + "agent.jar" + }; + Exception e = assertThrows(Exception.class, () -> findAgentJarFromCmdline(asList(cmdlineArgs))); + // The exception should contain some message indicating agent.jar was not found. + assertTrue(e.getMessage().contains("agent.jar")); + } +} diff --git a/java_agent/agent-framework/agent-api/pom.xml b/java_agent/agent-framework/agent-api/pom.xml new file mode 100644 index 0000000..d034433 --- /dev/null +++ b/java_agent/agent-framework/agent-api/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-api + agent-api API for implementing hooks + + jar + + + + io.prometheus + simpleclient_common + + + diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java new file mode 100644 index 0000000..c3f73e4 --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/After.java @@ -0,0 +1,17 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The method annotated with @After is executed before exiting the instrumented method. + * The method annotated with @After must have exactly the same parameters as the instrumented method. + * The "method" parameter are the names of the instrumented methods. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface After { + String[] method(); +} diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java new file mode 100644 index 0000000..d3cab3b --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Before.java @@ -0,0 +1,17 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The method annotated with @Before is executed before entering the instrumented method. + * The method annotated with @Before must have exactly the same parameters as the instrumented method. + * The "method" parameter are the names of the instrumented methods. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Before { + String[] method(); +} diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java new file mode 100644 index 0000000..a1b0622 --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Hook.java @@ -0,0 +1,29 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The @Hook annotation indicates that the annotated class is a Hook. + * The parameter {@link Hook#instruments()} defines which classes or interfaces are instrumented by that Hook. + * The method annotations {@link Before} and {@link After} define which methods should be instrumented within that class or interface. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Hook { + + /** + * List of classes or interfaces to be instrumented. + */ + String[] instruments(); + + /** + * If true, nested calls are skipped. + * If set to {@code false}, nested calls will also be instrumented. + * For nested calls, the same Hook instance is re-used. + * For outer calls, a new Hook instance is created. + */ + boolean skipNestedCalls() default true; +} diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java new file mode 100644 index 0000000..12033c4 --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Returned.java @@ -0,0 +1,18 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A parameter annotated with @Returned can be used in a Hook's @After method to capture the return value of the instrumented method. + * + * Example: In order to instrument the following method: + * The parameter annotated with @Returned is optional, if the hook does not use the return value, the parameter can be omitted. + * If the instrumented method terminates exceptionally, the type's default value is assigned to the parameter, + * i.e. {@code 0} for numeric types and {@code null} for reference types. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Returned {} diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java new file mode 100644 index 0000000..c82ccd5 --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/annotations/Thrown.java @@ -0,0 +1,33 @@ +package io.agent.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * A parameter annotated with @Thrown can be used in a Hook's @After method to capture an Exception thrown in an instrumented method. + * + * Example: In order to instrument the following method: + * + *

+ *     int div(int a, int b) {
+ *         return a / b;
+ *     }
+ * 
+ * + * A Hook could use an @After method like this: + * + *
+ *    {@literal @}After(method = "div")
+ *     void after(int a, int b, @Returned int result, @Thrown Throwable exception) {...}
+ * 
+ * + * In case everything goes well, the {@code result} will be the return value of {@code div()}, and {@code excption} will be {@code null}. + * If {@code b} is {@code 0}, the {@code result} will be {@code 0}, and {@code exception} will be an {@link ArithmeticException} (division by zero). + *

+ * The parameter annotated with @Thrown is optional, if the hook does not use the exception, the parameter can be omitted. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface Thrown {} diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java new file mode 100644 index 0000000..42655b8 --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricDef.java @@ -0,0 +1,38 @@ +package io.agent.hookcontext; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; + +import java.util.function.BiFunction; + +/** + * See {@link #MetricDef(String, BiFunction)} + */ +public class MetricDef { + + private final String metricName; + private final BiFunction producer; + + /** + * See {@link MetricsStore}. + * @param metricName Name of the Prometheus metric, like "http_requests_total". + * @param producer Function to create a new metric and register it with the registry. + * This function will only be called once, subsequent calls to {@link MetricsStore#createOrGet(MetricDef)} + * will return the previously created metric with the specified name. + * The two parameters are the metricName as specified above, + * and the Prometheus registry where the new metric should be registered. + * For an example see JavaDoc for {@link MetricsStore}. + */ + public MetricDef(String metricName, BiFunction producer) { + this.metricName = metricName; + this.producer = producer; + } + + String getMetricName() { + return metricName; + } + + BiFunction getProducer() { + return producer; + } +} diff --git a/java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java b/java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java new file mode 100644 index 0000000..f14f085 --- /dev/null +++ b/java_agent/agent-framework/agent-api/src/main/java/io/agent/hookcontext/MetricsStore.java @@ -0,0 +1,38 @@ +package io.agent.hookcontext; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.BiFunction; + +/** + * Instead of creating Prometheus Metrics directly, Hooks should use the {@link MetricsStore} like this: + *

+ * Counter httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>(
+ *         "http_requests_total",
+ *         (name, registry) -> Counter.build()
+ *                 .name(name)
+ *                 .labelNames("method", "path", "status")
+ *                 .help("Total number of http requests.")
+ *                 .register(registry)
+ * ));
+ * 
+ * The Promgent framework will take care that each metric is created only once and re-used across re-deployments in an application server. + */ +public class MetricsStore { + + private final CollectorRegistry registry; + private final ConcurrentMap metrics = new ConcurrentHashMap<>(); + + public MetricsStore(CollectorRegistry registry) { + this.registry = registry; + } + + // See {@link MetricsStore} and {@link MetricDef#MetricDef(String, BiFunction)}. + @SuppressWarnings("unchecked") + public T createOrGet(MetricDef metricDef) { + return (T) metrics.computeIfAbsent(metricDef.getMetricName(), s -> metricDef.getProducer().apply(metricDef.getMetricName(), registry)); + } +} diff --git a/java_agent/agent-framework/agent-exporter/pom.xml b/java_agent/agent-framework/agent-exporter/pom.xml new file mode 100644 index 0000000..dd57d88 --- /dev/null +++ b/java_agent/agent-framework/agent-exporter/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-exporter + agent.war + + + false + + + war + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + agent + + diff --git a/java_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java b/java_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java new file mode 100644 index 0000000..b8d2ac7 --- /dev/null +++ b/java_agent/agent-framework/agent-exporter/src/main/java/io/agent/exporter/agentExporterServlet.java @@ -0,0 +1,29 @@ +package io.agent.exporter; + +import javax.management.ObjectName; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.management.ManagementFactory; + +/** + * This servlet simply calls the ExporterMBean via JMX and provides the result. + */ +@WebServlet("/") +public class agentExporterServlet extends HttpServlet { + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + String result = (String) ManagementFactory.getPlatformMBeanServer() + .getAttribute(new ObjectName("io.agent:type=exporter"), "TextFormat"); + response.getWriter().println(result); + } catch (Exception e) { + response.setStatus(500); + response.getWriter().println("Failed to load Exporter MBean. Are you sure the agent is running?"); + e.printStackTrace(); + } + } +} diff --git a/java_agent/agent-framework/agent-internal/pom.xml b/java_agent/agent-framework/agent-internal/pom.xml new file mode 100644 index 0000000..25e1e4f --- /dev/null +++ b/java_agent/agent-framework/agent-internal/pom.xml @@ -0,0 +1,69 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-internal + agent-internal classes shared across deployments + + jar + + + + + ${project.groupId} + agent-agent + ${project.version} + provided + + + + ${project.groupId} + agent-api + ${project.version} + provided + + + + net.bytebuddy + byte-buddy + 1.9.4 + + + + commons-io + commons-io + 2.6 + + + + io.prometheus + simpleclient_common + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.mockito + mockito-core + 2.23.0 + test + + + + javax.servlet + javax.servlet-api + 4.0.1 + test + + + + diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java new file mode 100644 index 0000000..d3bb002 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/BuiltInServer.java @@ -0,0 +1,57 @@ + + +package io.agent.internal; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import java.io.IOException; +import java.io.StringWriter; +import java.net.InetSocketAddress; +import java.util.Collections; + +/** + * Use the Java runtime's built-in {@link HttpServer} to export Prometheus metrics. + */ +class BuiltInServer { + + static void run(String host, String portString, CollectorRegistry registry) throws Exception { + try { + int port = Integer.parseInt(portString); + InetSocketAddress address = host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port); + HttpServer httpServer = HttpServer.create(address, 10); + httpServer.createContext("/", httpExchange -> { + if ("/metrics".equals(httpExchange.getRequestURI().getPath())) { + respondMetrics(registry, httpExchange); + } else { + respondRedirect(httpExchange); + } + }); + httpServer.start(); + } catch (NumberFormatException e) { + throw new RuntimeException("Failed to parse command line arguments: '" + portString + "' is not a valid port number."); + } + } + + private static void respondMetrics(CollectorRegistry registry, HttpExchange httpExchange) throws IOException { + StringWriter respBodyWriter = new StringWriter(); + respBodyWriter.write("# Metrics will become visible when they are updated for the first time.\n"); + TextFormat.write004(respBodyWriter, registry.metricFamilySamples()); + byte[] respBody = respBodyWriter.toString().getBytes("UTF-8"); + httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); + httpExchange.sendResponseHeaders(200, respBody.length); + httpExchange.getResponseBody().write(respBody); + httpExchange.getResponseBody().close(); + } + + private static void respondRedirect(HttpExchange httpExchange) throws IOException { + byte[] respBody = "Metrics are provided on the /metrics endpoint.".getBytes("UTF-8"); + httpExchange.getResponseHeaders().add("Location", "/metrics"); + httpExchange.getResponseHeaders().put("Context-Type", Collections.singletonList("text/plain; charset=UTF-8")); + httpExchange.sendResponseHeaders(302, respBody.length); + httpExchange.getResponseBody().write(respBody); + httpExchange.getResponseBody().close(); + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java new file mode 100644 index 0000000..de5dd09 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/Delegator.java @@ -0,0 +1,264 @@ + + +package io.agent.internal; + +import io.agent.agent.ClassLoaderCache; +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Returned; +import io.agent.annotations.Thrown; +import io.agent.hookcontext.MetricsStore; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Delegator is called from the Byte Buddy Advice, and calls the Hook's @Before and @After methods. + *

+ * TODO: This is called often, should be performance optimized, e.g. caching hook method handles, etc. + */ +public class Delegator { + + private static Delegator instance; // not thread-safe, but it is set only once in the agent's premain method. + + private final SortedSet hookMetadata; + private final MetricsStore metricsStore; + private final ClassLoaderCache classLoaderCache; + private final ThreadLocal, Object>> threadLocal; + + private Delegator(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { + this.hookMetadata = hookMetadata; + this.metricsStore = metricsStore; + this.classLoaderCache = classLoaderCache; + this.threadLocal = ThreadLocal.withInitial(HashMap::new); + } + + public static void init(SortedSet hookMetadata, MetricsStore metricsStore, ClassLoaderCache classLoaderCache) { + instance = new Delegator(hookMetadata, metricsStore, classLoaderCache); + } + + /** + * Should be called from the Advice's @OnMethodEnter method. Returns the list of Hooks to be passed on to after() + */ + public static List before(Class interceptedClass, Method interceptedMethod, Object[] args) { + return instance.doBefore(interceptedClass, interceptedMethod, args); + } + + private List doBefore(Class interceptedClass, Method interceptedMethod, Object[] args) { + List hookInstances = loadFromThreadLocalOrCreate(interceptedClass, interceptedMethod); + for (HookInstance hookInstance : hookInstances) { + invokeBefore(hookInstance.getInstance(), interceptedMethod, args); + } + return hookInstances; + } + + /** + * Should be called from the Advice's @OnMethodExit method. First parameter is the list of hooks returned by before() + */ + public static void after(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { + instance.doAfter(hookInstances, interceptedMethod, args, returned, thrown); + } + + private void doAfter(List hookInstances, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) { + if (hookInstances != null) { + for (HookInstance hookInstance : hookInstances) { + invokeAfter(hookInstance.getInstance(), interceptedMethod, args, returned, thrown); + if (!hookInstance.isRecursiveCall()) { + threadLocal.get().remove(hookInstance.getInstance().getClass()); + } + } + } + } + + private List loadFromThreadLocalOrCreate(Class interceptedClass, Method interceptedMethod) { + return hookMetadata.stream() + .filter(hook -> classOrInterfaceMatches(interceptedClass, hook)) + .filter(hook -> methodNameAndNumArgsMatch(interceptedMethod, hook)) + .map(hook -> loadHookClass(hook)) + .filter(hookClass -> argumentTypesMatch(hookClass, interceptedMethod)) + .filter(hookClass -> ! shouldBeSkipped(hookClass)) + .map(hookClass -> loadFromTheadLocalOrCreate(hookClass)) + .collect(Collectors.toList()); + } + + private static boolean classOrInterfaceMatches(Class classToBeInstrumented, HookMetadata hook) { + Set classesAndInterfaces = getAllSuperClassesAndInterfaces(classToBeInstrumented); + return hook.getInstruments().stream().anyMatch(classesAndInterfaces::contains); + } + + private static Set getAllSuperClassesAndInterfaces(Class clazz) { + Set result = new HashSet<>(); + addAllSuperClassesAndInterfaces(clazz, result); + return result; + } + + private static void addAllSuperClassesAndInterfaces(Class clazz, Set result) { + if (clazz == null) { + return; + } + if (result.contains(clazz.getName())) { + return; + } + result.add(clazz.getName()); + for (Class ifc : clazz.getInterfaces()) { + addAllSuperClassesAndInterfaces(ifc, result); + } + addAllSuperClassesAndInterfaces(clazz.getSuperclass(), result); + } + + private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata hook) { + return hook.getMethods().stream() + .anyMatch(instrumentedMethod -> methodNameAndNumArgsMatch(interceptedMethod, instrumentedMethod)); + } + + private static boolean methodNameAndNumArgsMatch(Method interceptedMethod, HookMetadata.MethodSignature instrumentedMethod) { + if (!interceptedMethod.getName().equals(instrumentedMethod.getMethodName())) { + return false; + } + if (interceptedMethod.getParameterCount() != instrumentedMethod.getParameterTypes().size()) { + return false; + } + return true; + } + + private Class loadHookClass(HookMetadata hook) { + try { + return classLoaderCache.currentClassLoader().loadClass(hook.getHookClassName()); + } catch (ClassNotFoundException e) { + throw new HookException("Failed to load Hook class " + hook.getHookClassName() + ": " + e.getMessage(), e); + } + } + + private static boolean argumentTypesMatch(Class hookClass, Method interceptedMethod) { + List before = findHookMethods(Before.class, hookClass, interceptedMethod); + List after = findHookMethods(After.class, hookClass, interceptedMethod); + return ! (before.isEmpty() && after.isEmpty()); + } + + private static List findHookMethods(Class annotation, Class hookClass, Method interceptedMethod) throws HookException { + return Stream.of(hookClass.getDeclaredMethods()) + .filter(method -> method.isAnnotationPresent(annotation)) + .filter(method -> getMethodNames(method.getAnnotation(annotation)).contains(interceptedMethod.getName())) + .filter(method -> parameterTypesMatch(method, interceptedMethod)) + .collect(Collectors.toList()); + } + + private static List getMethodNames(Annotation annotation) throws HookException { + if (Before.class.isAssignableFrom(annotation.getClass())) { + return Arrays.asList(((Before) annotation).method()); + } else if (After.class.isAssignableFrom(annotation.getClass())) { + return Arrays.asList(((After) annotation).method()); + } else { + throw new HookException("Unsupported Annotation: @" + annotation.getClass().getSimpleName() + "."); + } + } + + // TODO: We could extend this to find the "closest" match, like in Java method calls. + private static boolean parameterTypesMatch(Method hookMethod, Method interceptedMethod) { + List> hookParameterTypes = stripReturnedAndThrown(hookMethod); + if (hookParameterTypes.size() != interceptedMethod.getParameterCount()) { + return false; + } + for (int i = 0; i < hookParameterTypes.size(); i++) { + Class hookParam = hookParameterTypes.get(i); + Class interceptedParam = interceptedMethod.getParameterTypes()[i]; + if (!hookParam.equals(interceptedParam)) { + return false; + } + } + return true; + } + + private static List> stripReturnedAndThrown(Method hookMethod) { + Class[] allTypes = hookMethod.getParameterTypes(); + Annotation[][] annotations = hookMethod.getParameterAnnotations(); + if (allTypes.length != annotations.length) { + throw new HookException("Method.getParameterAnnotations() returned an unexpected value. This is a bug in agent."); + } + List> result = new ArrayList<>(); + for (int i=0; i Returned.class.equals(a) || Thrown.class.equals(a))) { + result.add(allTypes[i]); + } + } + return result; + } + + private boolean shouldBeSkipped(Class hookClass) { + return hookClass.getAnnotation(io.agent.annotations.Hook.class).skipNestedCalls() + && threadLocal.get().containsKey(hookClass); + } + + private HookInstance loadFromTheadLocalOrCreate(Class hookClass) { + Object existingHookInstance = threadLocal.get().get(hookClass); + if (existingHookInstance != null) { + return new HookInstance(existingHookInstance, true); + } else { + String errMsg = "Failed to create new instance of hook " + hookClass.getSimpleName() + ": "; + try { + Object newHookInstance = hookClass.getConstructor(MetricsStore.class).newInstance(metricsStore); + threadLocal.get().put(hookClass, newHookInstance); + return new HookInstance(newHookInstance, false); + } catch (NoSuchMethodException e) { + throw new HookException(errMsg + "Hook classes must have a public constructor with a single parameter of type " + MetricsStore.class.getSimpleName(), e); + } catch (Exception e) { + throw new HookException(errMsg + e.getMessage(), e); + } + } + } + + /** + * Invoke the matching Hook methods annotated with @Before + */ + private static void invokeBefore(Object hookInstance, Method interceptedMethod, Object[] args) throws HookException { + invoke(Before.class, hookInstance, interceptedMethod, args, null, null); + } + + /** + * Invoke the matching Hook methods annotated with @After + */ + private static void invokeAfter(Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { + invoke(After.class, hookInstance, interceptedMethod, args, returned, thrown); + } + + private static void invoke(Class annotation, Object hookInstance, Method interceptedMethod, Object[] args, Object returned, Throwable thrown) throws HookException { + if (args.length != interceptedMethod.getParameterCount()) { + throw new IllegalArgumentException("Number of provided arguments is " + args.length + ", but interceptedMethod expects " + interceptedMethod.getParameterCount() + " argument(s)."); + } + for (Method method : findHookMethods(annotation, hookInstance.getClass(), interceptedMethod)) { + try { + if (!method.isAccessible()) { + method.setAccessible(true); + } + method.invoke(hookInstance, addReturnedAndThrownArgs(method, args, returned, thrown)); + } catch (Exception e) { + throw new HookException("Failed to call " + method.getName() + "() on " + hookInstance.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + } + + private static Object[] addReturnedAndThrownArgs(Method hookMethod, Object[] args, Object returned, Throwable thrown) { + Annotation[][] annotations = hookMethod.getParameterAnnotations(); + List result = new ArrayList<>(); + int arg = 0; + for (Annotation[] annotation : annotations) { + if (Arrays.stream(annotation) + .map(Annotation::annotationType) + .anyMatch(Returned.class::equals)) { + result.add(returned); + } else if (Arrays.stream(annotation) + .map(Annotation::annotationType) + .anyMatch(Thrown.class::equals)) { + result.add(thrown); + } else { + result.add(args[arg++]); + } + } + return result.toArray(); + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java new file mode 100644 index 0000000..dfaf80c --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookException.java @@ -0,0 +1,12 @@ +package io.agent.internal; + +public class HookException extends RuntimeException { + + public HookException(String message) { + super(message); + } + + public HookException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java new file mode 100644 index 0000000..73c2f20 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookInstance.java @@ -0,0 +1,20 @@ +package io.agent.internal; + +public class HookInstance { + + private final Object instance; + private final boolean isRecursiveCall; // true if we have taken an existing instance from the ThreadLocal + + public HookInstance(Object instance, boolean isRecursiveCall) { + this.instance = instance; + this.isRecursiveCall = isRecursiveCall; + } + + public Object getInstance() { + return instance; + } + + public boolean isRecursiveCall() { + return isRecursiveCall; + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java new file mode 100644 index 0000000..21652d5 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadata.java @@ -0,0 +1,128 @@ + +package io.agent.internal; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Metadata for a Hook class as parsed from the @Hook, @Before, and @After annotations. + */ +public class HookMetadata implements Comparable { + + private final String hookClassName; + private final SortedSet instruments; + private final SortedSet methods; + + public static class MethodSignature implements Comparable { + + private final String methodName; + private final List parameterTypes; + + public MethodSignature(String methodName, List parameterTypes) { + this.methodName = methodName; + this.parameterTypes = Collections.unmodifiableList(new ArrayList<>(parameterTypes)); + } + + public String getMethodName() { + return methodName; + } + + public List getParameterTypes() { + return parameterTypes; + } + + @Override + public String toString() { + return methodName + "(" + String.join(", ", parameterTypes) + ")"; + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && compareTo((MethodSignature) o) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(toString()); + } + + @Override + public int compareTo(MethodSignature o) { + return Comparator + .comparing(MethodSignature::getMethodName) + .thenComparing(MethodSignature::getParameterTypes, new LexicographicalComparator<>()) + .compare(this, o); + } + } + + public HookMetadata(String hookClassName, Collection instruments, Collection methods) { + this.hookClassName = hookClassName; + this.instruments = Collections.unmodifiableSortedSet(new TreeSet<>(instruments)); + this.methods = Collections.unmodifiableSortedSet(new TreeSet<>(methods)); + } + + public String getHookClassName() { + return hookClassName; + } + + public SortedSet getInstruments() { + return instruments; + } + + public SortedSet getMethods() { + return methods; + } + + @Override + public String toString() { // TODO: instruments is a Set + String delimiter = System.lineSeparator() + " * "; + return hookClassName + " instruments [" + String.join(", ", instruments) + "]:" + delimiter + String.join(delimiter, strings(methods)); + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass() && compareTo((HookMetadata) o) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(toString()); + } + + @Override + public int compareTo(HookMetadata o) { + return Comparator + .comparing(HookMetadata::getHookClassName) + .thenComparing(HookMetadata::getInstruments, new LexicographicalComparator<>()) + .thenComparing(HookMetadata::getMethods, new LexicographicalComparator<>()) + .compare(this, o); + } + + private List strings(Collection list) { + return list.stream().map(Object::toString).collect(Collectors.toList()); + } + + /** + * Compare SortedSet instances or List instances in lexicographical order. + */ + static class LexicographicalComparator, T extends Iterable> implements Comparator { + @Override + public int compare(T list1, T list2) { + Iterator iterator1 = list1.iterator(); + Iterator iterator2 = list2.iterator(); + while (iterator1.hasNext() && iterator2.hasNext()) { + int result = iterator1.next().compareTo(iterator2.next()); + if (result != 0) { + return result; + } + } + if (iterator1.hasNext()) { + return 1; + } + if (iterator2.hasNext()) { + return -1; + } + return 0; + } + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java new file mode 100644 index 0000000..2723b10 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/HookMetadataParser.java @@ -0,0 +1,321 @@ +package io.agent.internal; + +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Returned; +import io.agent.annotations.Thrown; +import io.agent.internal.HookMetadata.MethodSignature; +import net.bytebuddy.jar.asm.*; +import org.apache.commons.io.IOUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Inspect all hook classes in a JAR file and load the metadata from the @Hook, @Before, and @After annotations. + *

+ * The tricky part is that we cannot create an instance of the hook classes, because they might reference + * classes that are not available in the agent's premain phase. We need to parse the metadata without instantiating + * the hook classes. + */ +public class HookMetadataParser { + + private final SortedSet hookJars; + + public HookMetadataParser(Collection hookJars) { + this.hookJars = Collections.unmodifiableSortedSet(new TreeSet<>(hookJars)); + } + + /** + * Get the {@link HookMetadata} for the hook classes without loading the hook classes. + *

+ * We unsuccessfully tried two alternative implementations before we ended up with the current one. + * The first attempt was to use reflection like this: + *

+     *     Class hookClass = HookConfig.class.getClassLoader().loadClass(hookClassName);
+     *     for (Method m : hookClass.getMethods()) {
+     *         // ... get methodConfig
+     *     }
+     * 
+ * However, this throws a ClassNotFoundException in hookClass.getMethods(), because + * a hook method may have parameters like ServletRequest that are not available during agent startup. + *

+ * The second attempt was to analyze the hook using Byte buddy like this: + *

+     *     TypeDescription typeDesc = typePool.describe(hookClassName).resolve();
+     *     for (MethodDescription methodDesc : typeDesc.getDeclaredMethods()) {
+     *         TypeList params = methodDesc.getParameters().asTypeList().asErasures();
+     *         // ... get methodConfig
+     *     }
+     * 
+ * However, this throws a ClassNotFoundException in methodDesc.getParameters().asTypeList(). + * We did not find any way in the Byte buddy API to access a method's parameter types without loading these types. + *

+ * The current implementation uses ASM directly to analyze the hook class's byte code. + */ + SortedSet parse() throws IOException, ClassNotFoundException { + return parse(className -> true); // Parse all classes found in the JAR files. + } + + /** + * See {@link #parse()}. + * + * The classNameFilter is used to parse only specific classes from the JAR files. + */ + public SortedSet parse(Predicate classNameFilter) throws IOException, ClassNotFoundException { + SortedSet result = new TreeSet<>(); + for (String className : listAllJavaClasses(hookJars, classNameFilter)) { + byte[] binaryRepresentation = readBinaryRepresentation(className); + ClassReader classReader = new ClassReader(binaryRepresentation); + HookMetadataBuilder hookMetadata = new HookMetadataBuilder(className); + classReader.accept(new ClassVisitor(Opcodes.ASM5) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && typeEquals(desc, io.agent.annotations.Hook.class)) { + return new AnnotationValueCollector("instruments", hookMetadata::addInstruments, Opcodes.ASM5, super.visitAnnotation(desc, visible)); + } else { + return super.visitAnnotation(desc, visible); + } + } + + @Override + public MethodVisitor visitMethod(int i, String method, String desc, String signature, String[] strings) { + List parameterTypes = Arrays.stream(Type.getArgumentTypes(desc)) + .map(Type::getClassName) + .collect(Collectors.toList()); + MethodSignatureBuilder builder = hookMetadata.newMethodSignature(parameterTypes); + return new MethodVisitor(Opcodes.ASM5, super.visitMethod(i, method, desc, signature, strings)) { + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { + if (visible && typeEquals(desc, Returned.class, Thrown.class)) { + builder.markReturnedOrThrown(parameter); + } + return super.visitParameterAnnotation(parameter, desc, visible); + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + if (visible && typeEquals(desc, Before.class, After.class)) { + return new AnnotationValueCollector("method", builder::addMethodName, Opcodes.ASM5, super.visitAnnotation(desc, visible)); + } else { + return super.visitAnnotation(desc, visible); + } + } + }; + } + + }, ClassReader.SKIP_CODE); + if (hookMetadata.isComplete()) { + result.add(hookMetadata.build()); + } + } + return result; + } + + private byte[] readBinaryRepresentation(String className) throws ClassNotFoundException { + String classFileName = "/" + className.replace(".", "/") + ".class"; + try(InputStream stream = getResourceAsStream(classFileName)) { + if (stream == null) { + throw new ClassNotFoundException(className); + } + return IOUtils.toByteArray(stream); + } catch (IOException e) { + throw new ClassNotFoundException(className, e); + } + } + + private InputStream getResourceAsStream(String name) throws IOException { + for (Path hookJar : hookJars) { + try { + URL url; + // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. + if (Files.isDirectory(hookJar)) { + url = hookJar.toUri().resolve("." + name).toURL(); + } else if (Files.isRegularFile(hookJar)) { + url = new URL("jar:" + hookJar.toUri().toURL().toString() + "!" + name); + } else { + throw new IOException("Invalid JAR file or classes directory: " + hookJar); + } + return url.openStream(); + } catch (FileNotFoundException e) {} + } + throw new FileNotFoundException(name + " not found in [" + hookJars.stream().map(Path::toString).reduce("", (s1, s2) -> s1 + ", " + s2) + "]"); + } + + private boolean typeEquals(String typeDescriptor, Class... classes) { + for (Class clazz : classes) { + if (clazz.getName().equals(Type.getType(typeDescriptor).getClassName())) { + return true; + } + } + return false; + } + + + /** + * Helper to create {@link HookMetadata} isntances. + */ + private static class HookMetadataBuilder { + + private String hookClassName; + private List instruments = new ArrayList<>(); + private List methods = new ArrayList<>(); + + private HookMetadataBuilder(String hookClassName) { + this.hookClassName = hookClassName; + } + + private void addInstruments(String instruments) { + this.instruments.add(instruments); + } + + private MethodSignatureBuilder newMethodSignature(List parameterTypes) { + MethodSignatureBuilder builder = new MethodSignatureBuilder(); + for (String parameterType : parameterTypes) { + builder.addParameterType(parameterType); + } + methods.add(builder); + return builder; + } + + private HookMetadata build() { + SortedSet methodSignatures = new TreeSet<>(); + for (MethodSignatureBuilder builder : methods) { + methodSignatures.addAll(builder.build()); + } + return new HookMetadata(hookClassName, instruments, methodSignatures); + } + + private boolean isComplete() { + return hookClassName != null && instruments.size() > 0 && methods.size() > 0; + } + } + + /** + * Helper to create {@link MethodSignature} instances. + *

+ * There's a slight mismatch between a {@link MethodSignatureBuilder} and a {@link MethodSignature}: + * While a {@link MethodSignature} can only contain a single method name, + * the {@link MethodSignatureBuilder} may have multiple method names. + * Therefore, the {@link #build()} method might create multiple {@link MethodSignature} instances + * out of a single {@link MethodSignatureBuilder}. + */ + private static class MethodSignatureBuilder { + + private static class ParameterType { + final String type; + boolean isReturnedOrThrown; // method parameters annotated with @Returned or @Thrown will be ignored. + + private ParameterType(String type) { + this.type = type; + this.isReturnedOrThrown = false; + } + } + + SortedSet methodNames = new TreeSet<>(); + List parameterTypes = new ArrayList<>(); + + private void addMethodName(String methodName) { + methodNames.add(methodName); + } + + private void addParameterType(String parameterType) { + parameterTypes.add(new ParameterType(parameterType)); + } + + private SortedSet build() { + List strippedParameterTypes = parameterTypes.stream() + .filter(p -> ! p.isReturnedOrThrown) + .map(p -> p.type) + .collect(Collectors.toList()); + SortedSet result = new TreeSet<>(); + for (String methodName : methodNames) { + result.add(new MethodSignature(methodName, strippedParameterTypes)); + } + return result; + } + + public void markReturnedOrThrown(int parameter) { + // We know that parameter is a valid index in parameterTypes. + parameterTypes.get(parameter).isReturnedOrThrown = true; + } + } + + /** + * Collect annotation values from @Hook(instruments = ...) or @Before(method = ...) or @After(method = ...) + *

+ * Example: If the annotation is @Before(method={"service", "doFilter"}) + * then using a AnnotationValueCollector with methodName=="method" will add the call + * consumer.accept("service") and consumer.accept("doFilter") + */ + private static class AnnotationValueCollector extends AnnotationVisitor { + private final Consumer consumer; + private final String methodName; + private AnnotationValueCollector(String methodName, Consumer consumer, int api, AnnotationVisitor av) { + super(api, av); + this.consumer = consumer; + this.methodName = methodName; + } + @Override + public AnnotationVisitor visitArray(String name) { + if (methodName.equals(name)) { + return new AnnotationVisitor(Opcodes.ASM5, super.visitArray(name)) { + @Override + public void visit(String name, Object value) { + consumer.accept(value.toString()); + } + }; + } else { + return super.visitArray(name); + } + } + } + + /** + * List all Java classes found in the JAR files. + * + */ + private static Set listAllJavaClasses(Set hookJars, Predicate classNameFilter) throws IOException { + Set result = new TreeSet<>(); + for (Path hookJar : hookJars) { + // For convenient testing, hookJar may be a classes/ directory instead of a JAR file. + if (hookJar.toFile().isDirectory()) { + try (Stream dirEntries = Files.walk(hookJar)) { + addClassNames(dirEntries.map(hookJar::relativize).map(Path::toString), result, classNameFilter); + } + } + else if (hookJar.toFile().isFile()) { + try (ZipFile zipFile = new ZipFile(hookJar.toFile())) { + addClassNames(zipFile.stream().map(ZipEntry::getName), result, classNameFilter); + } + } else { + throw new IOException(hookJar + ": Failed to read file or directory."); + } + } + return result; + } + + /** + * Convert class file paths to class names and add them to result. + */ + private static void addClassNames(Stream paths, Collection result, Predicate classNameFilter) { + paths + .filter(name -> name.endsWith(".class")) + .map(name -> name.substring(0, name.length() - ".class".length())) + .map(name -> name.startsWith("/") ? name.substring(1) : name) + .map(name -> name.replace("/", ".")) + .filter(classNameFilter) + .collect(Collectors.toCollection(() -> result)); + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java new file mode 100644 index 0000000..4e9ac04 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agent.java @@ -0,0 +1,141 @@ +package io.agent.internal; + +import static net.bytebuddy.matcher.ElementMatchers.isAbstract; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import javax.management.ObjectName; +import java.lang.instrument.Instrumentation; +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeMap; +import java.util.TreeSet; + +import io.agent.agent.ClassLoaderCache; +import io.agent.hookcontext.MetricsStore; +import io.agent.internal.HookMetadata.MethodSignature; +import io.agent.internal.jmx.Exporter; +import io.agent.internal.jmx.agentCollectorRegistry; +import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import net.bytebuddy.matcher.ElementMatchers; + +public class agent { + + public static void premain(String agentArgs, Instrumentation inst) { + try { + agentCollectorRegistry registry = new agentCollectorRegistry(); + ManagementFactory.getPlatformMBeanServer().registerMBean(new Exporter(registry), new ObjectName("io.agent:type=exporter")); + Map args = parseCmdline(agentArgs); + if (args.containsKey("port")) { + BuiltInServer.run(args.get("host"), args.get("port"), registry); + } + ClassLoaderCache classLoaderCache = ClassLoaderCache.getInstance(); + List hookJars = classLoaderCache.getPerDeploymentJars(); + SortedSet hookMetadata = new HookMetadataParser(hookJars).parse(); + MetricsStore metricsStore = new MetricsStore(registry); + Delegator.init(hookMetadata, metricsStore, classLoaderCache); + printHookMetadata(hookMetadata); + + AgentBuilder agentBuilder = new AgentBuilder.Default(); + agentBuilder = applyHooks(agentBuilder, hookMetadata, classLoaderCache); + agentBuilder + .disableClassFormatChanges() + // .with(AgentBuilder.Listener.StreamWriting.toSystemError()) // use this to see exceptions thrown in instrumented code + .with(AgentBuilder.RedefinitionStrategy.REDEFINITION) + .with(AgentBuilder.TypeStrategy.Default.REDEFINE) + .installOn(inst); + + // TODO -- the following is an experiment supporting collectors directly (in addition to hooks) + // io.prometheus.client.Collector jmxCollector = (io.prometheus.client.Collector) classLoaderCache.currentClassLoader().loadClass("io.agent.collectors.JmxCollector").newInstance(); + // registry.registerNoJmx(jmxCollector); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * Add {@link ElementMatcher} for the hooks. + */ + private static AgentBuilder applyHooks(AgentBuilder agentBuilder, SortedSet hookMetadata, ClassLoaderCache classLoaderCache) { + Map> instruments = getInstruments(hookMetadata); + for (Map.Entry> entry : instruments.entrySet()) { + String instrumentedClassName = entry.getKey(); + Set instrumentedMethods = entry.getValue(); + agentBuilder = agentBuilder + .type(ElementMatchers.hasSuperType(named(instrumentedClassName))) + .transform(new AgentBuilder.Transformer.ForAdvice() + .include(classLoaderCache.currentClassLoader()) // must be able to load agentAdvice + .advice(matchAnyMethodIn(instrumentedMethods), agentAdvice.class.getName()) + ); + } + return agentBuilder; + } + + /** + * key: name of instrumented class or interface, value: set of instrumented methods for that class or interface + */ + public static Map> getInstruments(Set hooks) { + Map> result = new TreeMap<>(); + for (HookMetadata hookMetadata : hooks) { + for (String instruments : hookMetadata.getInstruments()) { + if (!result.containsKey(instruments)) { + result.put(instruments, new TreeSet<>()); + } + result.get(instruments).addAll(hookMetadata.getMethods()); + } + } + return result; + } + + /** + * Returns a byte buddy matcher matching any method contained in methodSignatures. + */ + public static ElementMatcher matchAnyMethodIn(Set methodSignatures) { + ElementMatcher.Junction methodMatcher = ElementMatchers.none(); + for (MethodSignature methodSignature : methodSignatures) { + ElementMatcher.Junction junction = ElementMatchers + .named(methodSignature.getMethodName()) + .and(not(isAbstract())) + .and(takesArguments(methodSignature.getParameterTypes().size())); + for (int i = 0; i < methodSignature.getParameterTypes().size(); i++) { + junction = junction.and(takesArgument(i, named(methodSignature.getParameterTypes().get(i)))); + } + methodMatcher = methodMatcher.or(junction); + } + return methodMatcher; + } + + /** + * Parse a comma-separated list of key/value pairs. Example: "host=localhost,port=9300" + */ + private static Map parseCmdline(String agentArgs) { + Map result = new HashMap<>(); + if (agentArgs != null) { + for (String keyValueString : agentArgs.split(",")) { + String[] keyValue = keyValueString.split("="); + if (keyValue.length != 2) { + throw new RuntimeException("Failed to parse command line arguments '" + agentArgs + "'. " + + "Expecting a comma-separated list of key/value pairs, as for example 'host=localhost,port=9300'."); + } + result.put(keyValue[0], keyValue[1]); + } + } + return result; + } + + private static void printHookMetadata(SortedSet hookMetadata) { + System.out.println("agent instrumenting the following classes or interfaces:"); + for (HookMetadata m : hookMetadata) { + System.out.println(m); + } + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java new file mode 100644 index 0000000..8566f3d --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/agentAdvice.java @@ -0,0 +1,64 @@ +package io.agent.internal; + +import io.agent.agent.ClassLoaderCache; +import net.bytebuddy.implementation.bytecode.assign.Assigner; + +import java.lang.reflect.Method; +import java.util.List; + +import static net.bytebuddy.asm.Advice.*; + +public class agentAdvice { + + // TODO: + // Should we move this class into it's own maven module + // to make clear that it cannot reference other classes from agent-internal? + + @OnMethodEnter + @SuppressWarnings("unchecked") + public static List before( + @This(optional = true) Object that, + @Origin Method method, + @AllArguments Object[] args + ) { + // that is null when instrumenting static methods. + Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); + try { + // The following code is equivalent to: + // return Delegator.before(that, method, args); + // However, the Delegator class will not be available in the context of the instrumented method, + // so we must use our agent class loader to load the Delegator class and do the call via reflection. + Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.agent.internal.Delegator"); + Method beforeMethod = delegator.getMethod("before", Class.class, Method.class, Object[].class); + return (List) beforeMethod.invoke(null, clazz, method, args); + } catch (Exception e) { + System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); + e.printStackTrace(); + return null; + } + } + + @OnMethodExit(onThrowable = Throwable.class) + public static void after( + @Enter List hooks, + @This(optional = true) Object that, + @Origin Method method, + @AllArguments Object[] args, + @Return(typing = Assigner.Typing.DYNAMIC) Object returned, // support void == null and int == Integer + @Thrown Throwable thrown + ) { + try { + // The following code is equivalent to: + // Delegator.after(hooks, method, args); + // However, the Delegator class will not be available in the context of the instrumented method, + // so we must use our agent class loader to load the Delegator class and do the call via reflection. + Class delegator = ClassLoaderCache.getInstance().currentClassLoader().loadClass("io.agent.internal.Delegator"); + Method afterMethod = delegator.getMethod("after", List.class, Method.class, Object[].class, Object.class, Throwable.class); + afterMethod.invoke(null, hooks, method, args, returned, thrown); + } catch (Exception e) { + Class clazz = that != null ? that.getClass() : method.getDeclaringClass(); + System.err.println("Error executing Prometheus hook on " + clazz.getSimpleName()); + e.printStackTrace(); + } + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java new file mode 100644 index 0000000..0a89b53 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Exporter.java @@ -0,0 +1,27 @@ +package io.agent.internal.jmx; + +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.exporter.common.TextFormat; + +import java.io.IOException; +import java.io.StringWriter; + +public class Exporter implements ExporterMBean { + + private final CollectorRegistry registry; + + public Exporter(CollectorRegistry registry) { + this.registry = registry; + } + + @Override + public String getTextFormat() { + try { + StringWriter result = new StringWriter(); + TextFormat.write004(result, registry.metricFamilySamples()); + return result.toString(); + } catch (IOException e) { + throw new RuntimeException("Unexpected error when writing metrics to a String: " + e.getMessage(), e); + } + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java new file mode 100644 index 0000000..1812b3d --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/ExporterMBean.java @@ -0,0 +1,5 @@ +package io.agent.internal.jmx; + +public interface ExporterMBean { + String getTextFormat(); +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java new file mode 100644 index 0000000..30f4742 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/Metric.java @@ -0,0 +1,35 @@ + + +package io.agent.internal.jmx; + +import io.prometheus.client.Collector; + +import java.util.HashMap; +import java.util.Map; + +public class Metric implements MetricMBean { + + private final Collector metric; + + Metric(Collector metric) { + this.metric = metric; + } + + /** + * @see MetricMBean#getValues() + */ + @Override + public Map, Double> getValues() { + Map, Double> result = new HashMap<>(); + for (Collector.MetricFamilySamples samples : metric.collect()) { + for (Collector.MetricFamilySamples.Sample sample : samples.samples) { + Map labels = new HashMap<>(); + for (int i = 0; i < sample.labelNames.size(); i++) { + labels.put(sample.labelNames.get(i), sample.labelValues.get(i)); + } + result.put(labels, sample.value); + } + } + return result; + } +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java new file mode 100644 index 0000000..151ca17 --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/MetricMBean.java @@ -0,0 +1,16 @@ + + +package io.agent.internal.jmx; + +import java.util.Map; + +public interface MetricMBean { + + /** + * Get the values in a representation that can be used in MXBeans in JMX. + *

+ * The result is a map of labels -> value. + * The labels themselves are represented as a key -> value map. + */ + Map, Double> getValues(); +} diff --git a/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java new file mode 100644 index 0000000..89764cd --- /dev/null +++ b/java_agent/agent-framework/agent-internal/src/main/java/io/agent/internal/jmx/agentCollectorRegistry.java @@ -0,0 +1,52 @@ +package io.agent.internal.jmx; + +import io.prometheus.client.Collector; +import io.prometheus.client.CollectorRegistry; +import io.prometheus.client.SimpleCollector; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; + +/** + * This is like the regular {@link CollectorRegistry}, except that when you {@link #register(Collector)} a metric, + * the metric will also be registered as an MBean in the JMX platform server. + */ +public class agentCollectorRegistry extends CollectorRegistry { + + @Override + public void register(Collector metric) { + super.register(metric); + try { + ManagementFactory.getPlatformMBeanServer().registerMBean(new Metric(metric), makeObjectName((SimpleCollector) metric)); + } catch (Exception e) { + throw new RuntimeException("Failed to register metric: " + e.getMessage(), e); + } + } + + private static ObjectName makeObjectName(SimpleCollector metric) throws MalformedObjectNameException { + return makeObjectName(getFullName(metric)); + } + + private static ObjectName makeObjectName(String fullname) throws MalformedObjectNameException { + return new ObjectName("io.agent:type=metrics,name=" + fullname); + } + + private static String getFullName(SimpleCollector metric) { + // Unfortunately, there is no public API to get the 'fullname' of a metric. We use reflection to get it anyway. + try { + Field field = SimpleCollector.class.getDeclaredField("fullname"); + field.setAccessible(true); + return (String) field.get(metric); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException("Failed to access " + metric.getClass().getName() + ".fullname. " + + "This is probably because the internal implementation of the client library has changed. " + + "You should adapt the agent accordingly.", e); + } + } + + public void registerNoJmx(Collector collector) { + super.register(collector); + } +} diff --git a/java_agent/agent-framework/agent-loader/pom.xml b/java_agent/agent-framework/agent-loader/pom.xml new file mode 100644 index 0000000..d4a1455 --- /dev/null +++ b/java_agent/agent-framework/agent-loader/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-loader + agent-loader attach agent to a running JVM + + jar + + + + org.junit.jupiter + junit-jupiter-api + test + + + + com.sun + tools + 1.8 + system + ${java.home}/../lib/tools.jar + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + io.agent.loader.agentLoader + + + + + + + diff --git a/java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java b/java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java new file mode 100644 index 0000000..e096f25 --- /dev/null +++ b/java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java @@ -0,0 +1,87 @@ +package io.agent.loader; + +import com.sun.tools.attach.AgentLoadException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; + +public class agentLoader { + + public static void main(String[] args) throws Exception { + int pid = getIntArg(args, "-pid"); + int port = getIntArg(args, "-port"); + String agentJar = getStringArg(args, "-agent"); + agentLoader.loadagent(agentJar, pid, port); + } + + private static void loadagent(String agentJar, int pid, int port) throws Exception { + VirtualMachineDescriptor vmd = findVirtualMachine(Integer.toString(pid)); + if (vmd == null) { + System.err.println("No Java process found with PID " + pid); + System.exit(-1); + } + VirtualMachine vm = null; + try { + vm = VirtualMachine.attach(vmd); + vm.loadAgent(agentJar, "port=" + port); + } catch (AgentLoadException e) { + System.err.println("Failed to attach agent: " + getMessage(e)); + } finally { + if (vm != null) { + vm.detach(); + } + } + } + + private static VirtualMachineDescriptor findVirtualMachine(String pid) { + for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { + if (vmd.id().equalsIgnoreCase(pid)) { + return vmd; + } + } + return null; + } + + private static String getMessage(AgentLoadException e) { + switch (e.getMessage()) { + case "-4": + return "Insuffient memory"; + case "100": + return "Agent JAR not found or no Agent-Class attribute"; + case "101": + return "Unable to add JAR file to system class path"; + case "102": + return "Agent JAR loaded but agent failed to initialize"; + default: + return e.getMessage(); + } + } + + private static int getIntArg(String[] args, String option) { + String stringArg = getStringArg(args, option); + try { + return Integer.parseInt(stringArg); + } catch (NumberFormatException e) { + System.err.println(option + " " + stringArg + ": invalid argument"); + System.exit(-1); + return 0; // will never happen + } + } + + private static String getStringArg(String[] args, String option) { + for (int pos : new int[]{0, 2, 4}) { + if (args.length < pos + 2) { + printUsageAndExit(); + } + if (option.equals(args[pos])) { + return args[pos+1]; + } + } + printUsageAndExit(); + return null; // will never happen + } + + private static void printUsageAndExit() { + System.err.println("Usage: java -cp $JAVA_HOME/lib/tools.jar:/path/to/agent-loader.jar io.agent.loader.agentLoader -agent /path/to/agent.jar -port 9300 -pid "); + System.exit(-1); + } +} diff --git a/java_agent/agent-framework/agent-maven-plugin/pom.xml b/java_agent/agent-framework/agent-maven-plugin/pom.xml new file mode 100644 index 0000000..d626cc6 --- /dev/null +++ b/java_agent/agent-framework/agent-maven-plugin/pom.xml @@ -0,0 +1,73 @@ + + 4.0.0 + + + io.agent + agent-framework + 1.0-SNAPSHOT + + + agent-maven-plugin + agent-maven-plugin + + maven-plugin + + + + + io.agent + agent-internal + 1.0-SNAPSHOT + runtime + + + + io.agent + agent-agent + 1.0-SNAPSHOT + runtime + + + + io.agent + agent-api + 1.0-SNAPSHOT + runtime + + + + org.apache.maven + maven-plugin-api + 3.6.0 + provided + + + + org.apache.maven + maven-core + 3.6.0 + provided + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + provided + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + agent + + + + + diff --git a/java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java b/java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java new file mode 100644 index 0000000..c7ee43c --- /dev/null +++ b/java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentDependencies.java @@ -0,0 +1,163 @@ +package io.agent.plugin; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.descriptor.PluginDescriptor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; + +import static java.util.stream.Collectors.toList; + +/** + * Hard-coded list of JAR files that must go into the agent.jar. + * Versions are resolved through Maven. + * If the user adds a duplicate JAR with a different version, the plugin fails. + */ +class AgentDependencies { + + private final String pluginGroupId; + private final List dependencies; + + /** + * Hard-coded dependencies without versions (versions are resolved dynamically). + */ + private static class ExpectedDependency { + + final String groupId; + final String artifactId; + + private ExpectedDependency(String groupId, String artifactId) { + this.groupId = groupId; + this.artifactId = artifactId; + } + } + + private AgentDependencies(String pluginGroupId, List dependencies) { + this.pluginGroupId = pluginGroupId; + this.dependencies = dependencies; + } + + static AgentDependencies init(PluginDescriptor pluginDescriptor) throws MojoExecutionException { + + String pluginGroupId = pluginDescriptor.getGroupId(); + String pluginArtifactId = pluginDescriptor.getArtifactId(); + + List expectedDependencies = Arrays.asList( + new ExpectedDependency(pluginGroupId, "agent-agent"), + new ExpectedDependency(pluginGroupId, "agent-internal"), + new ExpectedDependency(pluginGroupId, "agent-api"), + new ExpectedDependency("io.prometheus", "simpleclient_common"), + new ExpectedDependency("io.prometheus", "simpleclient"), + new ExpectedDependency("net.bytebuddy", "byte-buddy"), + new ExpectedDependency("commons-io", "commons-io") + ); + + List actualDependencies = resolveVersions(pluginDescriptor, pluginArtifactId, expectedDependencies); + failUnlessComplete(actualDependencies, expectedDependencies, pluginArtifactId); + return new AgentDependencies(pluginGroupId, actualDependencies); + } + + /** + * Artifact for the agent-agent module. + */ + Artifact getAgentArtifact() { + return dependencies.stream() + .filter(isAgent()) + .findFirst() + .get(); // We know it's present. + } + + /** + * Artifact for all other runtime dependencies except agent-agent. + */ + List getDependencies() { + return dependencies.stream() + .filter(isAgent().negate()) + .collect(toList()); + } + + void assertNoConflict(Artifact artifact) throws MojoExecutionException { + Optional builtInVersion = dependencies.stream() + .filter(dependency -> dependency.getGroupId().equals(artifact.getGroupId())) + .filter(dependency -> dependency.getArtifactId().equals(artifact.getArtifactId())) + .map(Artifact::getVersion) + .findFirst(); + if (builtInVersion.isPresent() && ! builtInVersion.get().equals(artifact.getVersion())) { + String artifactName = artifact.getGroupId() + ":" + artifact.getArtifactId(); + throw new MojoExecutionException("Conflicting dependencies: Your project includes " + artifactName + + " version " + artifact.getVersion() + " but the agent-maven-plugin is built with version " + builtInVersion.get()); + } + } + + private static List resolveVersions(PluginDescriptor pluginDescriptor, String pluginArtifactId, List expectedDependencies) throws MojoExecutionException { + List actualDependencies = new ArrayList<>(); + for (Artifact artifact : pluginDescriptor.getArtifacts()) { + if (! isExpected(artifact, expectedDependencies)) { + continue; + } + if (isKnown(artifact, actualDependencies)) { + continue; + } + failOnVersionConflict(artifact, actualDependencies, pluginArtifactId); + actualDependencies.add(artifact); + } + return actualDependencies; + } + + private Predicate isAgent() { + return artifact -> artifact.getGroupId().equals(pluginGroupId) && artifact.getArtifactId().equals("agent-agent"); + } + + + private static Predicate expectedDependencyMatcher(Artifact artifact) { + return expectedDependency -> expectedDependency.groupId.equals(artifact.getGroupId()) && + expectedDependency.artifactId.equals(artifact.getArtifactId()); + } + + private static Predicate expectedDependencyMatcher(ExpectedDependency expectedDependency) { + return artifact -> expectedDependencyMatcher(artifact).test(expectedDependency); + } + + private static Predicate artifactMatcherWithoutVersion(Artifact artifact) { + return other -> artifact.getGroupId().equals(other.getGroupId()) && + artifact.getArtifactId().equals(other.getArtifactId()); + } + + private static Predicate artifactMatcherWithVersion(Artifact artifact) { + return other -> artifactMatcherWithoutVersion(artifact).test(other) && + artifact.getVersion().equals(other.getVersion()); + } + + private static boolean isExpected(Artifact artifact, List expectedDependencies) { + return expectedDependencies.stream().anyMatch(expectedDependencyMatcher(artifact)); + } + + private static boolean isKnown(Artifact artifact, List knownArtifacts) { + return knownArtifacts.stream().anyMatch(artifactMatcherWithVersion(artifact)); + } + + private static void failOnVersionConflict(Artifact artifact, List knownArtifacts, String pluginArtifactId) throws MojoExecutionException { + Optional conflictingVersion = knownArtifacts.stream() + .filter(artifactMatcherWithoutVersion(artifact)) + .filter(artifactMatcherWithVersion(artifact).negate()) // same version -> not conflicting + .findFirst() + .map(Artifact::getVersion); + if (conflictingVersion.isPresent()) { + String artifactName = artifact.getGroupId() + artifact.getArtifactId(); + throw new MojoExecutionException("version conflict in " + pluginArtifactId + ": " + artifactName + " found in version " + artifact.getVersion() + " and version " + conflictingVersion.get()); + } + } + + private static void failUnlessComplete(List actualDependencies, List expectedDependencies, String pluginArtifactId) throws MojoExecutionException { + for (ExpectedDependency expected : expectedDependencies) { + if (actualDependencies.stream().noneMatch(expectedDependencyMatcher(expected))) { + String dependencyName = expected.groupId + ":" + expected.artifactId; + throw new MojoExecutionException("Plugin dependency " + dependencyName + " missing. This is a bug in " + pluginArtifactId + "."); + } + } + } +} diff --git a/java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java b/java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java new file mode 100644 index 0000000..aa21063 --- /dev/null +++ b/java_agent/agent-framework/agent-maven-plugin/src/main/java/io/agent/plugin/AgentJar.java @@ -0,0 +1,121 @@ +package io.agent.plugin; + +import org.apache.maven.plugin.MojoExecutionException; +import org.codehaus.plexus.util.IOUtil; + +import java.io.*; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +class AgentJar implements AutoCloseable { + + private final String jarName; + private final JarOutputStream jarOutputStream; + private final Set content = new HashSet<>(); + + enum Directory { + SHARED_JARS("shared-jars/"), // Directory entries in JAR file must end in '/' + PER_DEPLOYMENT_JARS("per-deployment-jars/"); + + private final String name; + + Directory(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + private AgentJar(String jarName, JarOutputStream jarOutputStream) { + this.jarName = jarName; + this.jarOutputStream = jarOutputStream; + } + + static AgentJar create(File jarFile) throws MojoExecutionException { + try { + JarOutputStream jarOutputStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile))); + return new AgentJar(jarFile.getName(), jarOutputStream); + } catch (IOException e) { + throw new MojoExecutionException("Error creating " + jarFile.getName() + ": " + e.getMessage(), e); + } + } + + void addFile(File srcFile, String targetFileName, Directory targetDir) throws MojoExecutionException { + String destPath = targetDir.getName() + targetFileName; + if (content.contains(destPath)) { + return; + } + makeDirsRecursively(destPath); + content.add(destPath); + try (InputStream in = new FileInputStream(srcFile)) { + jarOutputStream.putNextEntry(new JarEntry(destPath)); + IOUtil.copy(in, jarOutputStream); + } catch (IOException e) { + throw new MojoExecutionException("Error adding " + srcFile.getName() + " to target JAR: " + e.getMessage(), e); + } + } + + void addFile(File srcFile, Directory targetDir) throws MojoExecutionException { + addFile(srcFile, srcFile.getName(), targetDir); + } + + void extractJar(File jar, ManifestTransformer manifestTransformer) throws MojoExecutionException { + try (JarFile jarFile = new JarFile(jar)) { + for (Enumeration jarEntries = jarFile.entries(); jarEntries.hasMoreElements(); ) { + JarEntry jarEntry = jarEntries.nextElement(); + if (manifestTransformer.canTransform(jarEntry)) { + jarEntry = manifestTransformer.transform(jarEntry); + } + if (!jarEntry.isDirectory() && !content.contains(jarEntry.getName())) { + content.add(jarEntry.getName()); + makeDirsRecursively(jarEntry.getName()); + try (InputStream in = getInputStream(jarEntry, jarFile, manifestTransformer)) { + jarOutputStream.putNextEntry(jarEntry); + IOUtil.copy(in, jarOutputStream); + } + } + } + } catch (IOException e) { + throw new MojoExecutionException("Error adding " + jar.getName() + " to target JAR: " + e.getMessage(), e); + } + } + + private InputStream getInputStream(JarEntry jarEntry, JarFile jarFile, ManifestTransformer manifestTransformer) throws IOException, MojoExecutionException { + InputStream in = jarFile.getInputStream(jarEntry); + if (manifestTransformer.canTransform(jarEntry)) { + in = manifestTransformer.transform(in); + } + return in; + } + + private void makeDirsRecursively(String path) throws MojoExecutionException { + String[] parts = path.split("/+"); + String segment = ""; + for (int i=0; i + 4.0.0 + + io.agent + agent-framework + 1.0-SNAPSHOT + + pom + agent framework + + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + UTF-8 + UTF-8 + 1.8 + 1.8 + 5.3.1 + + + + + + agent-loader + + agent-agent + agent-api + agent-internal + agent-exporter + agent-loader + agent-maven-plugin + + + + + default + + true + + + agent-agent + agent-api + agent-internal + agent-exporter + agent-maven-plugin + + + + + + + + + io.prometheus + simpleclient_common + 0.5.0 + + + org.junit.jupiter + junit-jupiter-api + ${junit5.version} + test + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 2.22.1 + + + + + diff --git a/java_agent/war_agent/pom-with-docker-tests.xml b/java_agent/war_agent/pom-with-docker-tests.xml new file mode 100644 index 0000000..94d727f --- /dev/null +++ b/java_agent/war_agent/pom-with-docker-tests.xml @@ -0,0 +1,386 @@ + + 4.0.0 + + io.agent + agent-example + 1.0-SNAPSHOT + + agent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.agent + agent-api + ${agent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + agent + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.22.1 + + + + + + io.agent + agent-maven-plugin + ${agent.framework.version} + + + agent + package + + build + + + + + + + + io.fabric8 + docker-maven-plugin + 0.27.2 + + true + + + + + ${project.artifactId}/oracle-jdk-8 + + ubuntu:17.10 + /opt + + apt-get update ; apt-get upgrade -y + apt-get install -y curl git neovim sudo + curl --silent --location --cookie "oraclelicense=accept-securebackup-cookie" -O http://download.oracle.com/otn-pub/java/jdk/8u192-b12/750e1c8617c5452694857ad95c3ee230/jdk-8u192-linux-x64.tar.gz + tar xfz jdk-8u192-linux-x64.tar.gz + curl --silent -O http://ftp.fau.de/apache/maven/maven-3/3.6.0/binaries/apache-maven-3.6.0-bin.tar.gz + tar xfz apache-maven-3.6.0-bin.tar.gz + + echo 'export JAVA_HOME="/opt/jdk1.8.0_192"' >> /etc/profile.d/java.sh ; \ + echo 'export MAVEN_HOME="/opt/apache-maven-3.6.0"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$JAVA_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; \ + echo 'export PATH="$MAVEN_HOME/bin:$PATH"' >> /etc/profile.d/java.sh ; + + adduser --disabled-password --gecos '' agent + echo 'agent ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + + agent + + /opt/jdk1.8.0_192 + /opt/apache-maven-3.6.0 + /opt/jdk1.8.0_192/bin:/opt/apache-maven-3.6.0/bin:$PATH + + + + + + + ${project.artifactId}/wildfly-kitchensink + + ${project.artifactId}/oracle-jdk-8 + /home/agent + + curl --silent -O http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.tar.gz + tar xfz wildfly-10.1.0.Final.tar.gz + rm wildfly-10.1.0.Final.tar.gz + git clone https://github.com/wildfly/quickstart.git + cd quickstart; git checkout 10.x + cd quickstart; mvn --batch-mode --projects kitchensink,helloworld --also-make package + mv quickstart/kitchensink/target/wildfly-kitchensink.war wildfly-10.1.0.Final/standalone/deployments + + mv quickstart/helloworld/target/wildfly-helloworld.war wildfly-10.1.0.Final/standalone/deployments + rm -rf ./quickstart ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/wildfly-kitchensink-agent + + ${project.artifactId}/wildfly-kitchensink + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'cd wildfly-10.1.0.Final' >> run.sh ; \ + echo 'LOGMANAGER_JAR=$(find $(pwd) -name "jboss-logmanager-*.jar")' >> run.sh ; \ + echo 'export JAVA_OPTS="' >> run.sh ; \ + echo ' -Djava.net.preferIPv4Stack=true' >> run.sh ; \ + echo ' -Xbootclasspath/p:${LOGMANAGER_JAR}' >> run.sh ; \ + echo ' -Djboss.modules.system.pkgs=org.jboss.logmanager,io.agent.agent' >> run.sh ; \ + echo ' -Djava.util.logging.manager=org.jboss.logmanager.LogManager' >> run.sh ; \ + echo ' -javaagent:../agent.jar=port=9300' >> run.sh ; \ + echo '"' >> run.sh ; \ + echo >> run.sh ; \ + echo './bin/standalone.sh -b=0.0.0.0' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/agent + agent:agent:agent + + + + target/agent.jar + / + + + + + + + + + + ${wildfly.port}:8080 + ${agent.port}:9300 + + + WildFly Full 10.1.0.Final .WildFly Core 2.2.0.Final. started in [0-9]+ms + + + ./run.sh + + true + [Wildfly] + + + + + + + ${project.artifactId}/spring + + ${project.artifactId}/oracle-jdk-8 + /home/agent + + git clone https://github.com/spring-guides/gs-accessing-data-rest.git + cd gs-accessing-data-rest/complete; mvn --batch-mode package + mv gs-accessing-data-rest/complete/target/gs-accessing-data-rest-*.jar . + rm -rf ./gs-accessing-data-rest ; rm -rf ./.m2 + + + + + + + ${project.artifactId}/spring-agent + + ${project.artifactId}/spring + + + echo '#!/bin/bash' >> run.sh ; \ + echo >> run.sh ; \ + echo 'java -javaagent:agent.jar=port=9300 -jar gs-accessing-data-rest-*.jar' >> run.sh ; \ + chmod 755 run.sh ; + + + + /home/agent + agent:agent:agent + + + + target/agent.jar + / + + + + + + + + + + ${spring.port}:8080 + ${agent.port}:9300 + + + Started Application in [0-9\.]+ seconds \(JVM running for [0-9\.]+\) + + + ./run.sh + + true + [Spring Boot] + + + + + + + + + start + pre-integration-test + + build + start + + + + stop + post-integration-test + + stop + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + none + + skip-docker-tests + + + + wildfly + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/wildfly-kitchensink, + ${project.artifactId}/wildfly-kitchensink-agent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/WildflyIT.java + + + + + http://${docker.host.address}:${wildfly.port}/wildfly-kitchensink + http://${docker.host.address}:${agent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + spring + + + ${project.artifactId}/oracle-jdk-8, + ${project.artifactId}/spring, + ${project.artifactId}/spring-agent + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + **/SpringIT.java + + + + + http://${docker.host.address}:${spring.port} + http://${docker.host.address}:${agent.port} + + + + + integration-tests + + integration-test + verify + + + + + + + + + diff --git a/java_agent/war_agent/pom.xml b/java_agent/war_agent/pom.xml new file mode 100644 index 0000000..3686b0b --- /dev/null +++ b/java_agent/war_agent/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + io.agent + agent-example + 1.0-SNAPSHOT + + agent example + + jar + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.0-SNAPSHOT + + + + + + io.agent + agent-api + ${agent.framework.version} + provided + + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + org.junit.jupiter + junit-jupiter-api + 5.3.1 + test + + + + com.squareup.okhttp3 + okhttp + 3.11.0 + test + + + + + + agent + + + io.agent + agent-maven-plugin + ${agent.framework.version} + + + agent + package + + build + + + + + + + + diff --git a/java_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java b/java_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java new file mode 100644 index 0000000..7c32b59 --- /dev/null +++ b/java_agent/war_agent/src/main/java/io/agent/collectors/JmxCollector.java @@ -0,0 +1,34 @@ +package io.agent.collectors; + +import com.sun.management.OperatingSystemMXBean; +import io.prometheus.client.Collector; + +import java.lang.management.ManagementFactory; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +// TODO -- the following is an experiment supporting collectors directly (in addition to hooks) +// TODO -- This class is not loaded by default, see commented-out lines in io.agent.internal.agent.premain() +// See JmxCollector in jmx_exporter +public class JmxCollector extends Collector implements Collector.Describable { + + @Override + public List collect() { + List result = new ArrayList<>(); + result.add(collectOperatingSystemMetrics()); + return Collections.unmodifiableList(result); + } + + @Override + public List describe() { + return new ArrayList<>(); + } + + private MetricFamilySamples collectOperatingSystemMetrics() { + OperatingSystemMXBean operatingSystemMXBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean(); + MetricFamilySamples.Sample cpuLoad = new MetricFamilySamples.Sample("process_cpu_load", new ArrayList<>(), new ArrayList<>(), operatingSystemMXBean.getProcessCpuLoad()); + return new MetricFamilySamples(cpuLoad.name, Type.GAUGE, "recent cpu usage for the whole system", Arrays.asList(cpuLoad)); + } +} diff --git a/java_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java b/java_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java new file mode 100644 index 0000000..a0c5168 --- /dev/null +++ b/java_agent/war_agent/src/main/java/io/agent/hooks/HttpContext.java @@ -0,0 +1,33 @@ +package io.agent.hooks; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +/** + * Store the HTTP context in a thread-local, so that we know which database call was triggered by which REST service. + */ +class HttpContext { + + static class Key {} + + static final Key HTTP_METHOD = new Key<>(); + static final Key HTTP_PATH = new Key<>(); + + private static final ThreadLocal> threadLocal = ThreadLocal.withInitial(HashMap::new); + + static void put(Key key, T value) { + threadLocal.get().put(key, value); + } + + @SuppressWarnings("unchecked") + static Optional get(Key key) { + return Optional.ofNullable((T) threadLocal.get().get(key)); + } + + static void clear(Key... keys) { + for (Key key : keys) { + threadLocal.get().remove(key); + } + } +} diff --git a/java_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java b/java_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java new file mode 100644 index 0000000..f701942 --- /dev/null +++ b/java_agent/war_agent/src/main/java/io/agent/hooks/JdbcHook.java @@ -0,0 +1,127 @@ +package io.agent.hooks; + +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Hook; +import io.agent.hookcontext.MetricDef; +import io.agent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import java.util.concurrent.TimeUnit; + +import static io.agent.hooks.HttpContext.HTTP_METHOD; +import static io.agent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "java.sql.Statement", + "java.sql.Connection" +}) +public class JdbcHook { + + private final Counter sqlQueriesTotal; + private final Summary sqlQueriesDuration; + private long startTime = 0; + + public JdbcHook(MetricsStore metricsStore) { + + sqlQueriesTotal = metricsStore.createOrGet(new MetricDef<>( + "sql_queries_total", + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "query") + .help("Total number of sql queries.") + .register(registry) + )); + + sqlQueriesDuration = metricsStore.createOrGet(new MetricDef<>( + "sql_query_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "query") + .help("Duration for serving the sql queries in seconds.") + .register(registry) + )); + } + + private String stripValues(String query) { + // We want the structure of the query as labels, not the actual values. + // Therefore, we replace: + // insert into Member (id, name, email, phone_number) values (0, 'John Smith', 'john.smith@mailinator.com', '2125551212') + // with + // insert into Member (id, name, email, phone_number) values (...) + return query.replaceAll("values\\s*\\(.*?\\)", "values (...)"); + } + + // --- before + + @Before(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void before(String sql) { + startTime = System.nanoTime(); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int autoGeneratedKeys) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, int[] columnIndexes) { + before(sql); + } + + @Before(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void before(String sql, String[] columnNames) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency) { + before(sql); + } + + @Before(method = {"prepareStatement", "prepareCall"}) + public void before(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) { + before(sql); + } + + // --- after + + @After(method = {"execute", "executeQuery", "executeUpdate", "executeLargeUpdate", "prepareStatement", "prepareCall"}) + public void after(String sql) throws Exception { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).orElse("no http context"); + String path = HttpContext.get(HTTP_PATH).orElse("no http context"); + String query = stripValues(sql); + sqlQueriesTotal.labels(method, path, query).inc(); + sqlQueriesDuration.labels(method, path, query).observe(duration); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int autoGeneratedKeys) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, int[] columnIndexes) throws Exception { + after(sql); + } + + @After(method = {"execute", "executeUpdate", "executeLargeUpdate", "prepareStatement"}) + public void after(String sql, String[] columnNames) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency) throws Exception { + after(sql); + } + + @After(method = {"prepareStatement", "prepareCall"}) + public void after(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws Exception { + after(sql); + } +} diff --git a/java_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java b/java_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java new file mode 100644 index 0000000..8c22295 --- /dev/null +++ b/java_agent/war_agent/src/main/java/io/agent/hooks/ServletHook.java @@ -0,0 +1,106 @@ +package io.agent.hooks; + +import io.agent.annotations.After; +import io.agent.annotations.Before; +import io.agent.annotations.Hook; +import io.agent.hookcontext.MetricDef; +import io.agent.hookcontext.MetricsStore; +import io.prometheus.client.Counter; +import io.prometheus.client.Summary; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.concurrent.TimeUnit; + +import static io.agent.hooks.HttpContext.HTTP_METHOD; +import static io.agent.hooks.HttpContext.HTTP_PATH; + +@Hook(instruments = { + "javax.servlet.Servlet", + "javax.servlet.Filter" +}) +public class ServletHook { + + private final Counter httpRequestsTotal; + private final Summary httpRequestsDuration; + private long startTime = 0; + + public ServletHook(MetricsStore metricsStore) { + + httpRequestsTotal = metricsStore.createOrGet(new MetricDef<>( + "http_requests_total", + (name, registry) -> Counter.build() + .name(name) + .labelNames("method", "path", "status") + .help("Total number of http requests.") + .register(registry) + )); + + httpRequestsDuration = metricsStore.createOrGet(new MetricDef<>( + "http_request_duration", + (name, registry) -> Summary.build() + .quantile(0.5, 0.05) // Add 50th percentile (= median) with 5% tolerated error + .quantile(0.9, 0.01) // Add 90th percentile with 1% tolerated error + .quantile(0.99, 0.001) // Add 99th percentile with 0.1% tolerated error + .name(name) + .labelNames("method", "path", "status") + .help("Duration for serving the http requests in seconds.") + .register(registry) + )); + } + + private String stripPathParameters(String path) { + + // The URL path may include path parameters. + // For example, REST URLs for querying an item might look like this: + // + // /item/1 + // /item/2 + // /item/3 + // etc. + // + // We don't want to create a new Prometheus label for each of these paths. + // Rather, we want a single label like this: + // + // /item/{id} + // + // This method replaces path parameters with placeholders. It is application specific and + // should be adapted depending on the actual paths in an application. + // For the demo, we just replace all numbers with {id}. + + return path + .replaceAll("/[0-9]+", "/{id}") + .replaceAll("/;jsessionid=\\w*", "") + .replaceAll("/$", "") + .replaceAll("\\?.*", ""); // Also remove path parameters, like "?jsessionid=..." + } + + @Before(method = {"service", "doFilter"}) + public void before(ServletRequest request, ServletResponse response) { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletRequest req = (HttpServletRequest) request; + HttpContext.put(HTTP_METHOD, req.getMethod()); + HttpContext.put(HTTP_PATH, stripPathParameters(req.getRequestURI())); + startTime = System.nanoTime(); + } + } + + // Return Werte und Exceptions als Parameter + @After(method = {"service", "doFilter"}) + public void after(ServletRequest request, ServletResponse response/*, @Returned int i, @Thrown Throwable t*/) throws Exception { + if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(response.getClass())) { + HttpServletResponse resp = (HttpServletResponse) response; + try { + double duration = ((double) System.nanoTime() - startTime) / (double) TimeUnit.SECONDS.toNanos(1L); + String method = HttpContext.get(HTTP_METHOD).get(); + String path = HttpContext.get(HTTP_PATH).get(); + httpRequestsTotal.labels(method, path, Integer.toString(resp.getStatus())).inc(); + httpRequestsDuration.labels(method, path, Integer.toString(resp.getStatus())).observe(duration); + } finally { + HttpContext.clear(HTTP_METHOD, HTTP_PATH); + } + } + } +} diff --git a/java_agent/war_agent/src/test/java/io/agent/it/SpringIT.java b/java_agent/war_agent/src/test/java/io/agent/it/SpringIT.java new file mode 100644 index 0000000..3337e0a --- /dev/null +++ b/java_agent/war_agent/src/test/java/io/agent/it/SpringIT.java @@ -0,0 +1,66 @@ +package io.agent.it; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.junit.jupiter.api.Test; + +public class SpringIT { + + /** + * Run some HTTP queries against a Docker container from image agent/spring-agent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pspring. + */ + @Test + public void testSpring() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request metricsRequest = new Request.Builder().url(System.getProperty("agent.url") + "/metrics").build(); + + // Execute two POST requests + Response restResponse = client.newCall(makePostRequest("Frodo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + restResponse = client.newCall(makePostRequest("Bilbo", "Baggins")).execute(); + assertEquals(201, restResponse.code()); + + // Query Prometheus metrics from agent + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .filter(m -> m.contains("status=\"201\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith("2.0"), "Value should be 2.0 for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + // The following regular expression tests for this string, but allows the parameters 'id', 'fist_name', 'last_name' to change order: + // query="insert into person (first_name, last_name, id)" + .filter(m -> m.matches(".*query=\"insert into person \\((?=.*id)(?=.*first_name)(?=.*last_name).*\\) values \\(...\\)\".*")) + .filter(m -> m.contains("method=\"POST\"")) + .filter(m -> m.contains("path=\"/people\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith("2.0"), "Value should be 2.0 for " + sqlQueriesTotal); + } + + private Request makePostRequest(String firstName, String lastName) { + return new Request.Builder() + .url(System.getProperty("deployment.url") + "/people") + .method("POST", RequestBody.create( + MediaType.parse("application/json"), + "{ \"firstName\" : \"" + firstName + "\", \"lastName\" : \"" + lastName + "\" }")) + .build(); + } +} diff --git a/java_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java b/java_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java new file mode 100644 index 0000000..9d9823a --- /dev/null +++ b/java_agent/war_agent/src/test/java/io/agent/it/WildflyIT.java @@ -0,0 +1,66 @@ +package io.agent.it; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class WildflyIT { + + /** + * Run some HTTP queries against a Docker container from image agent/wildfly-kitchensink-agent. + *

+ * The Docker container is started by the maven-docker-plugin when running mvn verify -Pwildfly. + */ + @Test + public void testWildfly() throws Exception { + OkHttpClient client = new OkHttpClient(); + Request restRequest = new Request.Builder().url(System.getProperty("deployment.url") + "/rest/members").build(); + + // Execute REST call + Response restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "1.0"); + + // Execute REST call again + restResponse = client.newCall(restRequest).execute(); + Assertions.assertEquals(restResponse.code(), 200); + Assertions.assertTrue(restResponse.body().string().contains("John Smith")); + + Thread.sleep(100); // metric is incremented after servlet has written the response, wait a little to get the updated metric + assertMetrics(client, "2.0"); + } + + private void assertMetrics(OkHttpClient client, String nCalls) throws Exception { + + Request metricsRequest = new Request.Builder().url(System.getProperty("agent.url") + "/metrics").build(); + Response metricsResponse = client.newCall(metricsRequest).execute(); + String[] metricsLines = metricsResponse.body().string().split("\n"); + + String httpRequestsTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("http_requests_total")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .filter(m -> m.contains("status=\"200\"")) + .findFirst().orElseThrow(() -> new Exception("http_requests_total metric not found.")); + + assertTrue(httpRequestsTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + httpRequestsTotal); + + String sqlQueriesTotal = Arrays.stream(metricsLines) + .filter(m -> m.contains("sql_queries_total")) + .filter(m -> m.matches(".*?query=\"select .*?id .*?email .*?name .*?phone_number .*? from Member .*?\".*?")) + .filter(m -> m.contains("method=\"GET\"")) + .filter(m -> m.contains("path=\"/wildfly-kitchensink/rest/members\"")) + .findFirst().orElseThrow(() -> new Exception("sql_queries_total metric not found.")); + + assertTrue(sqlQueriesTotal.endsWith(nCalls), "Value should be " + nCalls + " for " + sqlQueriesTotal); + } +} From 2364fe7ecb896ad9c6b8eea5ced2483b8d456d40 Mon Sep 17 00:00:00 2001 From: Vaibhav Agochiya Date: Mon, 18 Feb 2019 15:07:53 +0530 Subject: [PATCH 32/38] Update pom.xml --- agentloader/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agentloader/pom.xml b/agentloader/pom.xml index 78539f4..a049c88 100644 --- a/agentloader/pom.xml +++ b/agentloader/pom.xml @@ -9,9 +9,9 @@ 1.0-SNAPSHOT - 1.6 - 1.6 + 1.8 + 1.8 - \ No newline at end of file + From 28b1a8e251e583b1778272f7359eb9d18b10c05b Mon Sep 17 00:00:00 2001 From: anu Date: Tue, 19 Feb 2019 09:57:31 +0530 Subject: [PATCH 33/38] java_agent refactored --- .../agent-framework/agent-loader/pom.xml | 47 ---------- .../java/io/agent/loader/agentLoader.java | 87 ------------------- 2 files changed, 134 deletions(-) delete mode 100644 java_agent/agent-framework/agent-loader/pom.xml delete mode 100644 java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java diff --git a/java_agent/agent-framework/agent-loader/pom.xml b/java_agent/agent-framework/agent-loader/pom.xml deleted file mode 100644 index d4a1455..0000000 --- a/java_agent/agent-framework/agent-loader/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - 4.0.0 - - - io.agent - agent-framework - 1.0-SNAPSHOT - - - agent-loader - agent-loader attach agent to a running JVM - - jar - - - - org.junit.jupiter - junit-jupiter-api - test - - - - com.sun - tools - 1.8 - system - ${java.home}/../lib/tools.jar - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - io.agent.loader.agentLoader - - - - - - - diff --git a/java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java b/java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java deleted file mode 100644 index e096f25..0000000 --- a/java_agent/agent-framework/agent-loader/src/main/java/io/agent/loader/agentLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.agent.loader; - -import com.sun.tools.attach.AgentLoadException; -import com.sun.tools.attach.VirtualMachine; -import com.sun.tools.attach.VirtualMachineDescriptor; - -public class agentLoader { - - public static void main(String[] args) throws Exception { - int pid = getIntArg(args, "-pid"); - int port = getIntArg(args, "-port"); - String agentJar = getStringArg(args, "-agent"); - agentLoader.loadagent(agentJar, pid, port); - } - - private static void loadagent(String agentJar, int pid, int port) throws Exception { - VirtualMachineDescriptor vmd = findVirtualMachine(Integer.toString(pid)); - if (vmd == null) { - System.err.println("No Java process found with PID " + pid); - System.exit(-1); - } - VirtualMachine vm = null; - try { - vm = VirtualMachine.attach(vmd); - vm.loadAgent(agentJar, "port=" + port); - } catch (AgentLoadException e) { - System.err.println("Failed to attach agent: " + getMessage(e)); - } finally { - if (vm != null) { - vm.detach(); - } - } - } - - private static VirtualMachineDescriptor findVirtualMachine(String pid) { - for (VirtualMachineDescriptor vmd : VirtualMachine.list()) { - if (vmd.id().equalsIgnoreCase(pid)) { - return vmd; - } - } - return null; - } - - private static String getMessage(AgentLoadException e) { - switch (e.getMessage()) { - case "-4": - return "Insuffient memory"; - case "100": - return "Agent JAR not found or no Agent-Class attribute"; - case "101": - return "Unable to add JAR file to system class path"; - case "102": - return "Agent JAR loaded but agent failed to initialize"; - default: - return e.getMessage(); - } - } - - private static int getIntArg(String[] args, String option) { - String stringArg = getStringArg(args, option); - try { - return Integer.parseInt(stringArg); - } catch (NumberFormatException e) { - System.err.println(option + " " + stringArg + ": invalid argument"); - System.exit(-1); - return 0; // will never happen - } - } - - private static String getStringArg(String[] args, String option) { - for (int pos : new int[]{0, 2, 4}) { - if (args.length < pos + 2) { - printUsageAndExit(); - } - if (option.equals(args[pos])) { - return args[pos+1]; - } - } - printUsageAndExit(); - return null; // will never happen - } - - private static void printUsageAndExit() { - System.err.println("Usage: java -cp $JAVA_HOME/lib/tools.jar:/path/to/agent-loader.jar io.agent.loader.agentLoader -agent /path/to/agent.jar -port 9300 -pid "); - System.exit(-1); - } -} From faf31c58408ba1f378c8438176253948fafac9cc Mon Sep 17 00:00:00 2001 From: anjalikali Date: Tue, 19 Feb 2019 10:46:55 +0530 Subject: [PATCH 34/38] Adding data collector --- dataCollectorFetcher | 1 + 1 file changed, 1 insertion(+) create mode 160000 dataCollectorFetcher diff --git a/dataCollectorFetcher b/dataCollectorFetcher new file mode 160000 index 0000000..b3abb4b --- /dev/null +++ b/dataCollectorFetcher @@ -0,0 +1 @@ +Subproject commit b3abb4b465df5a8775fc34153327526e2454ff6e From c7885a8f94fdf06677b9c2556a0c03f29ace586b Mon Sep 17 00:00:00 2001 From: anjalikali Date: Tue, 19 Feb 2019 10:51:25 +0530 Subject: [PATCH 35/38] Readding Collector --- dataCollectorFetcher | 1 - 1 file changed, 1 deletion(-) delete mode 160000 dataCollectorFetcher diff --git a/dataCollectorFetcher b/dataCollectorFetcher deleted file mode 160000 index b3abb4b..0000000 --- a/dataCollectorFetcher +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b3abb4b465df5a8775fc34153327526e2454ff6e From 227b5fb288d7825b14e9af81650996476d76ba13 Mon Sep 17 00:00:00 2001 From: anjalikali Date: Tue, 19 Feb 2019 10:51:51 +0530 Subject: [PATCH 36/38] Readding Collector --- dataCollectorFetcher/Dockerfile | 5 ++ dataCollectorFetcher/pom.xml | 64 ++++++++++++++ .../Config/AutoWiringSpringBean.java | 33 +++++++ .../Controller/dataCollectorController.java | 51 +++++++++++ .../DataCollectorApplication.java | 20 +++++ .../KafkaConfig/KafkaConfiguration.java | 34 ++++++++ .../anjali/dataCollector/Model/AgentUrl.java | 16 ++++ .../Model/DataCollectorModel.java | 53 ++++++++++++ .../Scheduler/QrtzScheduler.java | 78 +++++++++++++++++ .../dataCollector/Scheduler/SampleJob.java | 30 +++++++ .../Scheduler/SpringQrtzScheduler.java | 85 +++++++++++++++++++ .../Service/SampleJobService.java | 46 ++++++++++ .../src/main/resources/application.properties | 2 + .../src/main/resources/quartz.properties | 10 +++ .../DataCollectorApplicationTests.java | 17 ++++ 15 files changed, 544 insertions(+) create mode 100644 dataCollectorFetcher/Dockerfile create mode 100644 dataCollectorFetcher/pom.xml create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Config/AutoWiringSpringBean.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/DataCollectorApplication.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/KafkaConfig/KafkaConfiguration.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/AgentUrl.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/DataCollectorModel.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/QrtzScheduler.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SpringQrtzScheduler.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java create mode 100644 dataCollectorFetcher/src/main/resources/application.properties create mode 100644 dataCollectorFetcher/src/main/resources/quartz.properties create mode 100644 dataCollectorFetcher/src/test/java/com/anjali/dataCollector/DataCollectorApplicationTests.java diff --git a/dataCollectorFetcher/Dockerfile b/dataCollectorFetcher/Dockerfile new file mode 100644 index 0000000..67eaa52 --- /dev/null +++ b/dataCollectorFetcher/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:11.0-oracle +ADD ./target/dataCollector-0.0.1-SNAPSHOT.jar /usr/src/dataCollector-0.0.1-SNAPSHOT.jar +WORKDIR /usr/src/ +EXPOSE 8001 +ENTRYPOINT ["java","-jar","dataCollector-0.0.1-SNAPSHOT.jar"] diff --git a/dataCollectorFetcher/pom.xml b/dataCollectorFetcher/pom.xml new file mode 100644 index 0000000..39460f9 --- /dev/null +++ b/dataCollectorFetcher/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.1.2.RELEASE + + + com.anjali + dataCollector + 0.0.1-SNAPSHOT + dataCollector + Data collector Service + + + 11 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.kafka + spring-kafka + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.kafka + spring-kafka-test + test + + + + org.springframework + spring-context-support + + + + org.quartz-scheduler + quartz + ${quartz.version} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Config/AutoWiringSpringBean.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Config/AutoWiringSpringBean.java new file mode 100644 index 0000000..9df8024 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Config/AutoWiringSpringBean.java @@ -0,0 +1,33 @@ +package com.anjali.dataCollector.Config; + +//public class AutoWiringSpringBean { +//} +import org.quartz.spi.TriggerFiredBundle; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.AutowireCapableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +/** + * Adds auto-wiring support to quartz jobs. + * @see "https://gist.github.com/jelies/5085593" + */ +public final class AutoWiringSpringBean extends SpringBeanJobFactory implements ApplicationContextAware { + + private transient AutowireCapableBeanFactory beanFactory; + + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + + beanFactory = applicationContext.getAutowireCapableBeanFactory(); + } + + @Override + protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { + + final Object job = super.createJobInstance(bundle); + beanFactory.autowireBean(job); + return job; + } + +} \ No newline at end of file diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java new file mode 100644 index 0000000..42a1b5f --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java @@ -0,0 +1,51 @@ +package com.anjali.dataCollector.Controller; + +import com.anjali.dataCollector.Model.AgentUrl; +import com.anjali.dataCollector.Model.DataCollectorModel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("data/") +public class dataCollectorController { + + private static final String TOPIC = "Kafka_Example_Test_Final"; + + @Autowired + private KafkaTemplate kafkaTemplate; + + private DataCollectorModel dataCollectorModel; + private ResponseEntity responseEntity = null; + + @Autowired + public dataCollectorController(DataCollectorModel dataCollectorModel) { + this.dataCollectorModel = dataCollectorModel; + } + + @GetMapping("hi") + public ResponseEntity check(){ + return new ResponseEntity ("Hello", HttpStatus.OK); + } + +// @PostMapping("api") +// public ResponseEntity collectMetric(@RequestBody AgentUrl agentUrl){ //request param +// String response = this.dataCollectorModel.getMetrics (agentUrl.getUrl()); +// +// this.responseEntity = new ResponseEntity<> (response, HttpStatus.OK); +// kafkaTemplate.send(TOPIC, response); +// return responseEntity; +// } + +} + + + + + + + + + diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/DataCollectorApplication.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/DataCollectorApplication.java new file mode 100644 index 0000000..a40e570 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/DataCollectorApplication.java @@ -0,0 +1,20 @@ +package com.anjali.dataCollector; + +import org.springframework.boot.Banner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.scheduling.annotation.EnableScheduling; + + +@EnableScheduling +@SpringBootApplication +public class DataCollectorApplication { + + public static void main(String[] args) { + + new SpringApplicationBuilder (DataCollectorApplication.class).bannerMode(Banner.Mode.OFF).run(args); + } + +} + diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/KafkaConfig/KafkaConfiguration.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/KafkaConfig/KafkaConfiguration.java new file mode 100644 index 0000000..d16e462 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/KafkaConfig/KafkaConfiguration.java @@ -0,0 +1,34 @@ +package com.anjali.dataCollector.KafkaConfig; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.core.DefaultKafkaProducerFactory; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.core.ProducerFactory; + +import java.util.HashMap; +import java.util.Map; + +//In Order to publish JSON messages below all configuration is required..........for Serialization purpose. + + @Configuration + public class KafkaConfiguration { + + @Bean + public ProducerFactory producerFactory() { + Map config = new HashMap<>(); + + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "172.23.239.85:9092"); + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + + return new DefaultKafkaProducerFactory<>(config); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } + +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/AgentUrl.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/AgentUrl.java new file mode 100644 index 0000000..bf6338a --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/AgentUrl.java @@ -0,0 +1,16 @@ +package com.anjali.dataCollector.Model; + +import org.springframework.stereotype.Component; + +@Component +public class AgentUrl { + private String url; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/DataCollectorModel.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/DataCollectorModel.java new file mode 100644 index 0000000..1a59629 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Model/DataCollectorModel.java @@ -0,0 +1,53 @@ +package com.anjali.dataCollector.Model; + +import org.springframework.stereotype.Component; + +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Scanner; + +@Component +public class DataCollectorModel { + + public String getMetrics(String inputUrl){ + String inline = ""; + + try { + URL url = new URL (inputUrl); + //Parse URL into HttpURLConnection in order to open the connection in order to get the JSON data + HttpURLConnection conn = (HttpURLConnection) url.openConnection (); + //Set the request to GET or POST as per the requirements + conn.setRequestMethod ("GET"); + //Use the connect method to create the connection bridge + conn.connect (); + //Get the response status of the Rest API + int responsecode = conn.getResponseCode (); + System.out.println ("Response code is: " + responsecode); + + //Iterating condition to if response code is not 200 then throw a runtime exception + //else continue the actual process of getting the JSON data + if (responsecode != 200) + throw new RuntimeException ("HttpResponseCode: " + responsecode); + else { + //Scanner functionality will read the JSON data from the stream + Scanner sc = new Scanner (url.openStream ()); + while (sc.hasNext ()) { + inline += sc.nextLine (); + } + System.out.println ("\nJSON Response in String format"); + System.out.println (inline); + //Close the stream when reading the data has been finished + sc.close (); + } + + + conn.disconnect (); + } + //Disconnect the HttpURLConnection stream + + catch (Exception e) { + e.printStackTrace (); + } + return inline; + } +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/QrtzScheduler.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/QrtzScheduler.java new file mode 100644 index 0000000..ea3e2e1 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/QrtzScheduler.java @@ -0,0 +1,78 @@ +package com.anjali.dataCollector.Scheduler; + +import static org.quartz.JobBuilder.newJob; +import static org.quartz.SimpleScheduleBuilder.simpleSchedule; +import static org.quartz.TriggerBuilder.newTrigger; + +import java.io.IOException; + +import javax.annotation.PostConstruct; + +import com.anjali.dataCollector.Config.AutoWiringSpringBean; +import org.quartz.*; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +@Configuration +@ConditionalOnExpression("'${using.spring.schedulerFactory}'=='false'") +public class QrtzScheduler { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ApplicationContext applicationContext; + + @PostConstruct + public void init() { + logger.info("Hello world from Quartz..."); + } + + @Bean + public SpringBeanJobFactory springBeanJobFactory() { + AutoWiringSpringBean jobFactory = new AutoWiringSpringBean (); + logger.debug("Configuring Job factory"); + + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean + public Scheduler scheduler(Trigger trigger, JobDetail job) throws SchedulerException, IOException { + + StdSchedulerFactory factory = new StdSchedulerFactory(); + factory.initialize(new ClassPathResource("quartz.properties").getInputStream()); + + logger.debug("Getting a handle to the Scheduler"); + Scheduler scheduler = factory.getScheduler(); + scheduler.setJobFactory(springBeanJobFactory()); + scheduler.scheduleJob(job, trigger); + + logger.debug("Starting Scheduler threads"); + scheduler.start(); + return scheduler; + } + + @Bean + public JobDetail jobDetail() { + + return newJob().ofType(SampleJob.class).storeDurably().withIdentity(JobKey.jobKey("Qrtz_Job_Detail")).withDescription("Invoke Sample Job service...").build(); + } + + @Bean + public Trigger trigger(JobDetail job) { + + int frequencyInSec = 10; + logger.info("Configuring trigger to fire every {} seconds", frequencyInSec); + + return newTrigger().forJob(job).withIdentity(TriggerKey.triggerKey("Qrtz_Trigger")).withDescription("Sample trigger").withSchedule(simpleSchedule().withIntervalInSeconds(frequencyInSec).repeatForever()).build(); + } +} + diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java new file mode 100644 index 0000000..f78cb0f --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java @@ -0,0 +1,30 @@ +package com.anjali.dataCollector.Scheduler; +import com.anjali.dataCollector.Service.SampleJobService; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SampleJob implements Job { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private SampleJobService jobService; + + public void execute(JobExecutionContext context) throws JobExecutionException { + + logger.info("Job ** {} ** fired @ {}", context.getJobDetail().getKey().getName(), context.getFireTime()); + + jobService.executeSampleJob(); + + + logger.info("Next job scheduled @ {}", context.getNextFireTime()); + logger.info("-------------anjali--------------"); + + } +} \ No newline at end of file diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SpringQrtzScheduler.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SpringQrtzScheduler.java new file mode 100644 index 0000000..00d087d --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SpringQrtzScheduler.java @@ -0,0 +1,85 @@ +package com.anjali.dataCollector.Scheduler; + + +import javax.annotation.PostConstruct; + +import com.anjali.dataCollector.Config.AutoWiringSpringBean; +import org.quartz.JobDetail; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.quartz.JobDetailFactoryBean; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; +import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; +import org.springframework.scheduling.quartz.SpringBeanJobFactory; + +@Configuration +@ConditionalOnExpression("'${using.spring.schedulerFactory}'=='true'") +public class SpringQrtzScheduler { + + Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ApplicationContext applicationContext; + + @PostConstruct + public void init() { + logger.info("Hello world from Spring..."); + } + + @Bean + public SpringBeanJobFactory springBeanJobFactory() { + AutoWiringSpringBean jobFactory = new AutoWiringSpringBean(); + logger.debug("Configuring Job factory"); + + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean + public SchedulerFactoryBean scheduler(Trigger trigger, JobDetail job) { + + SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean(); + schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties")); + + logger.debug("Setting the Scheduler up"); + schedulerFactory.setJobFactory(springBeanJobFactory()); + schedulerFactory.setJobDetails(job); + schedulerFactory.setTriggers(trigger); + + return schedulerFactory; + } + + @Bean + public JobDetailFactoryBean jobDetail() { + + JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean(); + jobDetailFactory.setJobClass(SampleJob.class); + jobDetailFactory.setName("Qrtz_Job_Detail"); + jobDetailFactory.setDescription("Invoke Sample Job service..."); + jobDetailFactory.setDurability(true); + return jobDetailFactory; + } + + @Bean + public SimpleTriggerFactoryBean trigger(JobDetail job) { + + SimpleTriggerFactoryBean trigger = new SimpleTriggerFactoryBean(); + trigger.setJobDetail(job); + + int frequencyInSec = 10; + logger.info("Configuring trigger to fire every {} seconds", frequencyInSec); + + trigger.setRepeatInterval(frequencyInSec * 1000); + trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY); + trigger.setName("Qrtz_Trigger"); + return trigger; + } +} \ No newline at end of file diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java new file mode 100644 index 0000000..1607ce9 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java @@ -0,0 +1,46 @@ +package com.anjali.dataCollector.Service; + +import com.anjali.dataCollector.Model.AgentUrl; +import com.anjali.dataCollector.Model.DataCollectorModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Service; + +@Service +public class SampleJobService { + + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private AgentUrl agentUrl; + + @Autowired + private DataCollectorModel dataCollectorModel; + + @Autowired + private KafkaTemplate kafkaTemplate; + + private static final String TOPIC = "Kafka_Example_Test_Final"; + + public void executeSampleJob() { + + logger.info("The sample job has begun..."); + +// Start Of Job + + agentUrl.setUrl ("http://172.23.239.162:8020/docker/stats"); + String response = dataCollectorModel.getMetrics (agentUrl.getUrl()); + kafkaTemplate.send(TOPIC, response); + +// End Of Job + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + logger.error("Error while executing sample job", e); + } finally { + logger.info("------------Sample job has finished-----------"); + } + } +} diff --git a/dataCollectorFetcher/src/main/resources/application.properties b/dataCollectorFetcher/src/main/resources/application.properties new file mode 100644 index 0000000..b03b207 --- /dev/null +++ b/dataCollectorFetcher/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.port=8001 +using.spring.schedulerFactory=true \ No newline at end of file diff --git a/dataCollectorFetcher/src/main/resources/quartz.properties b/dataCollectorFetcher/src/main/resources/quartz.properties new file mode 100644 index 0000000..cefaaef --- /dev/null +++ b/dataCollectorFetcher/src/main/resources/quartz.properties @@ -0,0 +1,10 @@ +# thread-pool +org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool +org.quartz.threadPool.threadCount=2 +org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true + +# job-store +org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore + +# others +org.quartz.jobStore.misfireThreshold = 60000 \ No newline at end of file diff --git a/dataCollectorFetcher/src/test/java/com/anjali/dataCollector/DataCollectorApplicationTests.java b/dataCollectorFetcher/src/test/java/com/anjali/dataCollector/DataCollectorApplicationTests.java new file mode 100644 index 0000000..ba5d33a --- /dev/null +++ b/dataCollectorFetcher/src/test/java/com/anjali/dataCollector/DataCollectorApplicationTests.java @@ -0,0 +1,17 @@ +package com.anjali.dataCollector; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class DataCollectorApplicationTests { + + @Test + public void contextLoads() { + } + +} + From d82d42bb1a817a84b9d972a86d0ae91d24fab8df Mon Sep 17 00:00:00 2001 From: swethadevid Date: Tue, 19 Feb 2019 14:58:07 +0530 Subject: [PATCH 37/38] removed field level autowiring --- .../com/stackroute/config/DatabaseLoader.java | 18 ++++++++++-------- .../stackroute/controller/UserController.java | 4 ++-- .../stackroute/service/UserServiceImpl.java | 6 +----- .../src/main/resources/application.properties | 2 +- .../src/main/resources/seedData.properties | 16 ++++++++-------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java b/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java index dc3d108..b909cb3 100644 --- a/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java +++ b/userservice/src/main/java/com/stackroute/config/DatabaseLoader.java @@ -19,21 +19,21 @@ public abstract class DatabaseLoader implements ApplicationListener"+username1); } diff --git a/userservice/src/main/java/com/stackroute/controller/UserController.java b/userservice/src/main/java/com/stackroute/controller/UserController.java index 74a50de..2ba6644 100644 --- a/userservice/src/main/java/com/stackroute/controller/UserController.java +++ b/userservice/src/main/java/com/stackroute/controller/UserController.java @@ -18,14 +18,14 @@ public class UserController { private static final String TOPIC = "Kafka_NewUser_Registration"; private UserService userService; - @Autowired - private KafkaTemplate kafkaTemplate; + private KafkaTemplate kafkaTemplate; @Autowired public UserController(UserService userService) { this.userService = userService; } + //Request mapping for posting user details @PostMapping("register") public ResponseEntity saveUser(@RequestBody User user) { diff --git a/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java b/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java index da513e6..3f9d7b4 100644 --- a/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java +++ b/userservice/src/main/java/com/stackroute/service/UserServiceImpl.java @@ -15,9 +15,6 @@ public class UserServiceImpl implements UserService { private UserRepository userRepository; -// @Autowired -// private PasswordEncoder passwordEncoder; - @Autowired public UserServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; @@ -29,8 +26,7 @@ public User saveUser(User user) throws UserAlreadyExistsException { throw new UserAlreadyExistsException("User already exists"); } - //Encrypting password - // user.setPassword(passwordEncoder.encode(user.getPassword())); + User user1 = userRepository.save(user); if (user1 == null) { throw new UserAlreadyExistsException("User already exists!!try to register new user"); diff --git a/userservice/src/main/resources/application.properties b/userservice/src/main/resources/application.properties index 98bda9b..2ed9e41 100644 --- a/userservice/src/main/resources/application.properties +++ b/userservice/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=8096 spring.datasource.driver-class-name=mongodb.jdbc.MongoDriver spring.data.mongodb.host=localhost spring.data.mongodb.port=27017 -spring.data.mongodb.database=syscopDB +spring.data.mongodb.database=syscopDB2 spring.data.mongodb.repositories.enabled=true diff --git a/userservice/src/main/resources/seedData.properties b/userservice/src/main/resources/seedData.properties index 217e4d1..f1d433c 100644 --- a/userservice/src/main/resources/seedData.properties +++ b/userservice/src/main/resources/seedData.properties @@ -1,8 +1,8 @@ -id1=1 -username1=Swetha Devi D -password1=swetha18boeing -phonenumber1=9909090900 -emailid1=swetha96devi@gmail.com -company1=BOEING -dob1=29/01/96 -role1=swetha +id2=1 +username2=Swetha Devi D +password2=swetha18boeing +phonenumber2=9909090900 +emailid2=swetha96devi@gmail.com +company2=BOEING +dob2=29/01/96 +role12=swetha From f559c101b80bc12d2657f2d06474acbac92c97eb Mon Sep 17 00:00:00 2001 From: anjalikali Date: Wed, 20 Feb 2019 12:11:18 +0530 Subject: [PATCH 38/38] adding Factory Classes and concrete class for metrics --- .../Controller/dataCollectorController.java | 9 ++------- .../dataCollector/FactoryModel/MetricFactory.java | 11 +++++++++++ .../dataCollector/MetricModel/DockerMetric.java | 4 ++++ .../anjali/dataCollector/MetricModel/JavaMetric.java | 4 ++++ .../dataCollector/MetricModel/MetricInterface.java | 7 +++++++ .../anjali/dataCollector/MetricModel/NodeMetric.java | 4 ++++ .../dataCollector/MetricModel/PythonMetric.java | 4 ++++ .../com/anjali/dataCollector/Scheduler/SampleJob.java | 1 - .../dataCollector/Service/SampleJobService.java | 4 ++-- 9 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/FactoryModel/MetricFactory.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/DockerMetric.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/JavaMetric.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/MetricInterface.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/NodeMetric.java create mode 100644 dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/PythonMetric.java diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java index 42a1b5f..0449aa5 100644 --- a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Controller/dataCollectorController.java @@ -1,9 +1,9 @@ package com.anjali.dataCollector.Controller; -import com.anjali.dataCollector.Model.AgentUrl; +//import com.anjali.dataCollector.Model.AgentUrl; import com.anjali.dataCollector.Model.DataCollectorModel; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; +//import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.web.bind.annotation.*; @@ -25,11 +25,6 @@ public dataCollectorController(DataCollectorModel dataCollectorModel) { this.dataCollectorModel = dataCollectorModel; } - @GetMapping("hi") - public ResponseEntity check(){ - return new ResponseEntity ("Hello", HttpStatus.OK); - } - // @PostMapping("api") // public ResponseEntity collectMetric(@RequestBody AgentUrl agentUrl){ //request param // String response = this.dataCollectorModel.getMetrics (agentUrl.getUrl()); diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/FactoryModel/MetricFactory.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/FactoryModel/MetricFactory.java new file mode 100644 index 0000000..c32bf73 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/FactoryModel/MetricFactory.java @@ -0,0 +1,11 @@ +package com.anjali.dataCollector.FactoryModel; + +public class MetricFactory { + + + public MetricFactory() { + + + + } +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/DockerMetric.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/DockerMetric.java new file mode 100644 index 0000000..5205f24 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/DockerMetric.java @@ -0,0 +1,4 @@ +package com.anjali.dataCollector.MetricModel; + +public class DockerMetric implements MetricInterface { +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/JavaMetric.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/JavaMetric.java new file mode 100644 index 0000000..4931ceb --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/JavaMetric.java @@ -0,0 +1,4 @@ +package com.anjali.dataCollector.MetricModel; + +public class JavaMetric implements MetricInterface { +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/MetricInterface.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/MetricInterface.java new file mode 100644 index 0000000..8ccf479 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/MetricInterface.java @@ -0,0 +1,7 @@ +package com.anjali.dataCollector.MetricModel; + +public interface MetricInterface { + + + +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/NodeMetric.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/NodeMetric.java new file mode 100644 index 0000000..0383ec5 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/NodeMetric.java @@ -0,0 +1,4 @@ +package com.anjali.dataCollector.MetricModel; + +public class NodeMetric implements MetricInterface{ +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/PythonMetric.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/PythonMetric.java new file mode 100644 index 0000000..1fe54e4 --- /dev/null +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/MetricModel/PythonMetric.java @@ -0,0 +1,4 @@ +package com.anjali.dataCollector.MetricModel; + +public class PythonMetric implements MetricInterface { +} diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java index f78cb0f..115ac89 100644 --- a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Scheduler/SampleJob.java @@ -24,7 +24,6 @@ public void execute(JobExecutionContext context) throws JobExecutionException { logger.info("Next job scheduled @ {}", context.getNextFireTime()); - logger.info("-------------anjali--------------"); } } \ No newline at end of file diff --git a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java index 1607ce9..eef4417 100644 --- a/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java +++ b/dataCollectorFetcher/src/main/java/com/anjali/dataCollector/Service/SampleJobService.java @@ -26,7 +26,7 @@ public class SampleJobService { public void executeSampleJob() { - logger.info("The sample job has begun..."); + logger.info("The docker job has begun..."); // Start Of Job @@ -40,7 +40,7 @@ public void executeSampleJob() { } catch (InterruptedException e) { logger.error("Error while executing sample job", e); } finally { - logger.info("------------Sample job has finished-----------"); + logger.info("---------The docker job has finished-----------"); } } }