Commit 6db18bb8 by wengbinbin
parents
运维平台和RESTFul API部署(可选)
解压缩elastic-job-lite-console-${version}.tar.gz并执行bin\start.sh。
打开浏览器访问http://localhost:8899/即可访问控制台。8899为默认端口号,可通过启动脚本输入-p自定义端口号。
访问RESTFul API方法同控制台。
elastic-job-lite-console-${version}.tar.gz可通过mvn install编译获取。
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: io.elasticjob:elastic-job-lite-lifecycle:3.0.0.M1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: io.elasticjob:elastic-job-lite-core:3.0.0.M1-SNAPSHOT" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.4" level="project" />
<orderEntry type="library" name="Maven: com.google.code.gson:gson:2.6.1" level="project" />
<orderEntry type="library" name="Maven: org.quartz-scheduler:quartz:2.3.0" level="project" />
<orderEntry type="library" name="Maven: com.mchange:mchange-commons-java:0.2.11" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP-java6:2.3.13" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-exec:1.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-framework:2.10.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-client:2.10.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.zookeeper:zookeeper:3.4.6" level="project" />
<orderEntry type="library" name="Maven: log4j:log4j:1.2.16" level="project" />
<orderEntry type="library" name="Maven: jline:jline:0.9.94" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty:3.7.0.Final" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:18.0" level="project" />
<orderEntry type="library" name="Maven: org.apache.curator:curator-recipes:2.10.0" level="project" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.10" level="project" />
<orderEntry type="library" name="Maven: com.sun.jersey:jersey-servlet:1.19" level="project" />
<orderEntry type="library" name="Maven: com.sun.jersey:jersey-server:1.19" level="project" />
<orderEntry type="library" name="Maven: com.sun.jersey:jersey-json:1.19" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jettison:jettison:1.1" level="project" />
<orderEntry type="library" name="Maven: com.sun.xml.bind:jaxb-impl:2.2.3-1" level="project" />
<orderEntry type="library" name="Maven: javax.xml.bind:jaxb-api:2.2.2" level="project" />
<orderEntry type="library" name="Maven: javax.xml.stream:stax-api:1.0-2" level="project" />
<orderEntry type="library" name="Maven: javax.activation:activation:1.1" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jackson:jackson-core-asl:1.9.2" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.2" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jackson:jackson-jaxrs:1.9.2" level="project" />
<orderEntry type="library" name="Maven: org.codehaus.jackson:jackson-xc:1.9.2" level="project" />
<orderEntry type="library" name="Maven: com.sun.jersey:jersey-core:1.19" level="project" />
<orderEntry type="library" name="Maven: javax.ws.rs:jsr311-api:1.1.1" level="project" />
<orderEntry type="library" name="Maven: com.sun.jersey:jersey-client:1.19" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.aggregate:jetty-all-server:8.1.19.v20160209" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.orbit:javax.servlet:3.0.0.v201112011016" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.orbit:javax.security.auth.message:1.0.0.v201108011116" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.orbit:javax.mail.glassfish:1.4.1.v201005082020" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.orbit:javax.activation:1.1.0.v201105071233" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.orbit:javax.annotation:1.1.0.v201108011116" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: org.projectlombok:lombok:1.16.4" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.7" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jcl-over-slf4j:1.7.7" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:log4j-over-slf4j:1.7.7" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.1.2" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.unitils:unitils-core:3.4.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: commons-logging:commons-logging:1.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: commons-lang:commons-lang:2.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: commons-collections:commons-collections:3.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: ognl:ognl:2.6.9" level="project" />
<orderEntry type="library" name="Maven: commons-dbcp:commons-dbcp:1.4" level="project" />
<orderEntry type="library" name="Maven: commons-pool:commons-pool:1.5.4" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.30" level="project" />
</component>
</module>
\ No newline at end of file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.elasticjob</groupId>
<artifactId>elastic-job-lite</artifactId>
<version>3.0.0.M1-SNAPSHOT</version>
</parent>
<artifactId>elastic-job-lite-console</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<dependencies>
<dependency>
<groupId>io.elasticjob</groupId>
<artifactId>elastic-job-lite-lifecycle</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>org.unitils</groupId>
<artifactId>unitils-core</artifactId>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>bin/*</exclude>
<exclude>conf/*</exclude>
<exclude>assembly/*</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/resources/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console;
import com.google.common.base.Optional;
import io.elasticjob.lite.console.filter.GlobalConfigurationFilter;
import io.elasticjob.lite.console.restful.JobOperationRestfulApi;
import io.elasticjob.lite.lifecycle.restful.RestfulServer;
import io.elasticjob.lite.lifecycle.security.WwwAuthFilter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 界面启动器.
*
* @author caohao
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public final class ConsoleBootstrap {
private static final String CONSOLE_PATH = "console";
/**
* 启动RESTful服务并加载页面.
*
* @param args 启动参数
* @throws Exception 启动服务器异常
*/
//CHECKSTYLE:OFF
public static void main(final String[] args) throws Exception {
//CHECKSTYLE:ON
int port = 8899;
if (1 == args.length) {
try {
port = Integer.parseInt(args[0]);
} catch (final NumberFormatException ex) {
log.warn("Wrong port format, using default port 8899 instead.");
}
}
RestfulServer restfulServer = new RestfulServer(port);
restfulServer.addFilter(GlobalConfigurationFilter.class, "*.html")
.addFilter(WwwAuthFilter.class, "/")
.addFilter(WwwAuthFilter.class, "*.html")
.start(JobOperationRestfulApi.class.getPackage().getName(), Optional.of(CONSOLE_PATH));
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.domain;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 事件追踪数据源.
*
* @author zhangxinguo
*/
@Slf4j
public final class EventTraceDataSource {
@Getter(AccessLevel.PROTECTED)
private EventTraceDataSourceConfiguration eventTraceDataSourceConfiguration;
public EventTraceDataSource(final EventTraceDataSourceConfiguration eventTraceDataSourceConfiguration) {
this.eventTraceDataSourceConfiguration = eventTraceDataSourceConfiguration;
}
/**
* 初始化.
*/
public void init() {
log.debug("Elastic job: data source init, connection url is: {}.", eventTraceDataSourceConfiguration.getUrl());
try {
Class.forName(eventTraceDataSourceConfiguration.getDriver());
DriverManager.getConnection(eventTraceDataSourceConfiguration.getUrl(), eventTraceDataSourceConfiguration.getUsername(), eventTraceDataSourceConfiguration.getPassword());
} catch (final ClassNotFoundException | SQLException ex) {
throw new RuntimeException(ex);
}
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.domain;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
/**
* 事件追踪数据源配置.
*
* @author zhangxinguo
*/
@NoArgsConstructor
@Getter
@Setter
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public final class EventTraceDataSourceConfiguration implements Serializable {
private static final long serialVersionUID = -5996257770767863699L;
@XmlAttribute(required = true)
private String name;
@XmlAttribute(required = true)
private String driver;
@XmlAttribute
private String url;
@XmlAttribute
private String username;
@XmlAttribute
private String password;
@XmlAttribute
private boolean activated;
public EventTraceDataSourceConfiguration(final String driver, final String url, final String username) {
this.driver = driver;
this.url = url;
this.username = username;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.domain;
import lombok.Getter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 事件追踪数据源配置根对象.
*
* @author zhangxinguo
*/
@Getter
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public final class EventTraceDataSourceConfigurations {
private Set<EventTraceDataSourceConfiguration> eventTraceDataSourceConfiguration = new LinkedHashSet<>();
}
package io.elasticjob.lite.console.domain;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.concurrent.ConcurrentHashMap;
/**
* 事件追踪数据源工厂.
*
* @author zhangxinguo
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class EventTraceDataSourceFactory {
private static final ConcurrentHashMap<HashCode, EventTraceDataSource> DATA_SOURCE_REGISTRY = new ConcurrentHashMap<>();
/**
* 创建事件追踪数据源.
*
* @param driver 数据库驱动类名称
* @param url 数据库URL
* @param username 数据库用户名
* @param password 数据库密码
* @return 事件追踪数据源
*/
public static EventTraceDataSource createEventTraceDataSource(final String driver, final String url, final String username, final Optional<String> password) {
Hasher hasher = Hashing.md5().newHasher().putString(driver, Charsets.UTF_8).putString(url, Charsets.UTF_8);
if (!Strings.isNullOrEmpty(username)) {
hasher.putString(username, Charsets.UTF_8);
}
if (password.isPresent()) {
hasher.putString(password.get(), Charsets.UTF_8);
}
HashCode hashCode = hasher.hash();
EventTraceDataSource result = DATA_SOURCE_REGISTRY.get(hashCode);
if (null != result) {
return result;
}
EventTraceDataSourceConfiguration eventTraceDataSourceConfiguration = new EventTraceDataSourceConfiguration(driver, url, username);
if (password.isPresent()) {
eventTraceDataSourceConfiguration.setPassword(password.get());
}
result = new EventTraceDataSource(eventTraceDataSourceConfiguration);
result.init();
DATA_SOURCE_REGISTRY.put(hashCode, result);
return result;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.domain;
import lombok.Getter;
import lombok.Setter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
/**
* 全局配置.
*
* @author zhangxinguo
*/
@Getter
@Setter
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public final class GlobalConfiguration {
private RegistryCenterConfigurations registryCenterConfigurations;
private EventTraceDataSourceConfigurations eventTraceDataSourceConfigurations;
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.domain;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
/**
* 注册中心配置.
*
* @author zhangliang
*/
@Getter
@Setter
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@NoArgsConstructor
public final class RegistryCenterConfiguration implements Serializable {
private static final long serialVersionUID = -5996257770767863699L;
@XmlAttribute(required = true)
private String name;
@XmlAttribute(required = true)
private String zkAddressList;
@XmlAttribute
private String namespace;
@XmlAttribute
private String digest;
@XmlAttribute
private boolean activated;
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.domain;
import lombok.Getter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* 注册中心配置根对象.
*
* @author zhangliang
*/
@Getter
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public final class RegistryCenterConfigurations {
private Set<RegistryCenterConfiguration> registryCenterConfiguration = new LinkedHashSet<>();
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.exception;
/**
* 控制台异常.
*
* @author zhangliang
*/
public final class JobConsoleException extends RuntimeException {
private static final long serialVersionUID = 1393957353478034407L;
public JobConsoleException(final Exception cause) {
super(cause);
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.filter;
import com.google.common.base.Optional;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfiguration;
import io.elasticjob.lite.console.domain.EventTraceDataSourceFactory;
import io.elasticjob.lite.console.domain.RegistryCenterConfiguration;
import io.elasticjob.lite.console.restful.config.EventTraceDataSourceRestfulApi;
import io.elasticjob.lite.console.restful.config.RegistryCenterRestfulApi;
import io.elasticjob.lite.console.service.EventTraceDataSourceConfigurationService;
import io.elasticjob.lite.console.service.RegistryCenterConfigurationService;
import io.elasticjob.lite.console.service.impl.EventTraceDataSourceConfigurationServiceImpl;
import io.elasticjob.lite.console.service.impl.RegistryCenterConfigurationServiceImpl;
import io.elasticjob.lite.console.util.SessionEventTraceDataSourceConfiguration;
import io.elasticjob.lite.console.util.SessionRegistryCenterConfiguration;
import io.elasticjob.lite.lifecycle.internal.reg.RegistryCenterFactory;
import io.elasticjob.lite.reg.exception.RegException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 全局配置过滤器.
*
* @author caohao
*/
public final class GlobalConfigurationFilter implements Filter {
private final RegistryCenterConfigurationService regCenterService = new RegistryCenterConfigurationServiceImpl();
private final EventTraceDataSourceConfigurationService rdbService = new EventTraceDataSourceConfigurationServiceImpl();
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpSession httpSession = httpRequest.getSession();
if (null == httpSession.getAttribute(RegistryCenterRestfulApi.REG_CENTER_CONFIG_KEY)) {
loadActivatedRegCenter(httpSession);
}
if (null == httpSession.getAttribute(EventTraceDataSourceRestfulApi.DATA_SOURCE_CONFIG_KEY)) {
loadActivatedEventTraceDataSource(httpSession);
}
filterChain.doFilter(servletRequest, servletResponse);
}
private void loadActivatedRegCenter(final HttpSession httpSession) {
Optional<RegistryCenterConfiguration> config = regCenterService.loadActivated();
if (config.isPresent()) {
String configName = config.get().getName();
boolean isConnected = setRegistryCenterNameToSession(regCenterService.find(configName, regCenterService.loadAll()), httpSession);
if (isConnected) {
regCenterService.load(configName);
}
}
}
private boolean setRegistryCenterNameToSession(final RegistryCenterConfiguration regCenterConfig, final HttpSession session) {
session.setAttribute(RegistryCenterRestfulApi.REG_CENTER_CONFIG_KEY, regCenterConfig);
try {
RegistryCenterFactory.createCoordinatorRegistryCenter(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
SessionRegistryCenterConfiguration.setRegistryCenterConfiguration((RegistryCenterConfiguration) session.getAttribute(RegistryCenterRestfulApi.REG_CENTER_CONFIG_KEY));
} catch (final RegException ex) {
return false;
}
return true;
}
private void loadActivatedEventTraceDataSource(final HttpSession httpSession) {
Optional<EventTraceDataSourceConfiguration> config = rdbService.loadActivated();
if (config.isPresent()) {
String configName = config.get().getName();
boolean isConnected = setEventTraceDataSourceNameToSession(rdbService.find(configName, rdbService.loadAll()), httpSession);
if (isConnected) {
rdbService.load(configName);
}
}
}
private boolean setEventTraceDataSourceNameToSession(final EventTraceDataSourceConfiguration dataSourceConfig, final HttpSession session) {
session.setAttribute(EventTraceDataSourceRestfulApi.DATA_SOURCE_CONFIG_KEY, dataSourceConfig);
try {
EventTraceDataSourceFactory.createEventTraceDataSource(dataSourceConfig.getDriver(), dataSourceConfig.getUrl(),
dataSourceConfig.getUsername(), Optional.fromNullable(dataSourceConfig.getPassword()));
SessionEventTraceDataSourceConfiguration.setDataSourceConfiguration((EventTraceDataSourceConfiguration) session.getAttribute(EventTraceDataSourceRestfulApi.DATA_SOURCE_CONFIG_KEY));
// CHECKSTYLE:OFF
} catch (final Exception ex) {
// CHECKSTYLE:ON
return false;
}
return true;
}
@Override
public void destroy() {
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.repository;
import io.elasticjob.lite.console.domain.GlobalConfiguration;
/**
* 基于XML的全局配置数据访问器.
*
* @author zhangliang
*/
public interface ConfigurationsXmlRepository extends XmlRepository<GlobalConfiguration> {
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.repository;
/**
* 基于XML的数据访问器.
*
* @author zhangliang
*
* @param <E> 数据类型
*/
public interface XmlRepository<E> {
/**
* 读取数据.
*
* @return 数据
*/
E load();
/**
* 存储数据.
*
* @param entity 数据
*/
void save(E entity);
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.repository.impl;
import io.elasticjob.lite.console.exception.JobConsoleException;
import io.elasticjob.lite.console.repository.XmlRepository;
import io.elasticjob.lite.console.util.HomeFolderUtils;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.File;
/**
* 基于XML的数据访问器实现类.
*
* @author zhangliang
*
* @param <E> 数据类型
*/
public abstract class AbstractXmlRepositoryImpl<E> implements XmlRepository<E> {
private final File file;
private final Class<E> clazz;
private JAXBContext jaxbContext;
protected AbstractXmlRepositoryImpl(final String fileName, final Class<E> clazz) {
file = new File(HomeFolderUtils.getFilePathInHomeFolder(fileName));
this.clazz = clazz;
HomeFolderUtils.createHomeFolderIfNotExisted();
try {
jaxbContext = JAXBContext.newInstance(clazz);
} catch (final JAXBException ex) {
throw new JobConsoleException(ex);
}
}
@Override
public synchronized E load() {
if (!file.exists()) {
try {
return clazz.newInstance();
} catch (final InstantiationException | IllegalAccessException ex) {
throw new JobConsoleException(ex);
}
}
try {
@SuppressWarnings("unchecked")
E result = (E) jaxbContext.createUnmarshaller().unmarshal(file);
return result;
} catch (final JAXBException ex) {
throw new JobConsoleException(ex);
}
}
@Override
public synchronized void save(final E entity) {
try {
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(entity, file);
} catch (final JAXBException ex) {
throw new JobConsoleException(ex);
}
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.repository.impl;
import io.elasticjob.lite.console.domain.GlobalConfiguration;
import io.elasticjob.lite.console.repository.ConfigurationsXmlRepository;
/**
* 基于XML的全局配置数据访问器实现类.
*
* @author zhangliang
*/
public final class ConfigurationsXmlRepositoryImpl extends AbstractXmlRepositoryImpl<GlobalConfiguration> implements ConfigurationsXmlRepository {
public ConfigurationsXmlRepositoryImpl() {
super("Configurations.xml", GlobalConfiguration.class);
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.restful;
import com.google.common.base.Strings;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfiguration;
import io.elasticjob.lite.console.service.EventTraceDataSourceConfigurationService;
import io.elasticjob.lite.console.service.impl.EventTraceDataSourceConfigurationServiceImpl;
import io.elasticjob.lite.console.util.SessionEventTraceDataSourceConfiguration;
import io.elasticjob.lite.event.rdb.JobEventRdbSearch;
import io.elasticjob.lite.event.type.JobExecutionEvent;
import io.elasticjob.lite.event.type.JobStatusTraceEvent;
import org.apache.commons.dbcp.BasicDataSource;
import javax.sql.DataSource;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 事件追踪历史记录的RESTful API.
*
* @author zhangxinguo
*/
@Path("/event-trace")
public final class EventTraceHistoryRestfulApi {
private EventTraceDataSourceConfiguration eventTraceDataSourceConfiguration = SessionEventTraceDataSourceConfiguration.getEventTraceDataSourceConfiguration();
private EventTraceDataSourceConfigurationService eventTraceDataSourceConfigurationService = new EventTraceDataSourceConfigurationServiceImpl();
/**
* 查询作业执行事件.
*
* @param uriInfo 查询条件
* @return 运行痕迹事件结果集
* @throws ParseException 解析异常
*/
@GET
@Path("/execution")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public JobEventRdbSearch.Result<JobExecutionEvent> findJobExecutionEvents(@Context final UriInfo uriInfo) throws ParseException {
if (!eventTraceDataSourceConfigurationService.loadActivated().isPresent()) {
return new JobEventRdbSearch.Result<>(0, new ArrayList<JobExecutionEvent>());
}
JobEventRdbSearch jobEventRdbSearch = new JobEventRdbSearch(setUpEventTraceDataSource());
return jobEventRdbSearch.findJobExecutionEvents(buildCondition(uriInfo, new String[]{"jobName", "ip", "isSuccess"}));
}
/**
* 查询作业状态事件.
*
* @param uriInfo 查询条件
* @return 运行痕迹事件结果集
* @throws ParseException 解析异常
*/
@GET
@Path("/status")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public JobEventRdbSearch.Result<JobStatusTraceEvent> findJobStatusTraceEvents(@Context final UriInfo uriInfo) throws ParseException {
if (!eventTraceDataSourceConfigurationService.loadActivated().isPresent()) {
return new JobEventRdbSearch.Result<>(0, new ArrayList<JobStatusTraceEvent>());
}
JobEventRdbSearch jobEventRdbSearch = new JobEventRdbSearch(setUpEventTraceDataSource());
return jobEventRdbSearch.findJobStatusTraceEvents(buildCondition(uriInfo, new String[]{"jobName", "source", "executionType", "state"}));
}
private DataSource setUpEventTraceDataSource() {
BasicDataSource result = new BasicDataSource();
result.setDriverClassName(eventTraceDataSourceConfiguration.getDriver());
result.setUrl(eventTraceDataSourceConfiguration.getUrl());
result.setUsername(eventTraceDataSourceConfiguration.getUsername());
result.setPassword(eventTraceDataSourceConfiguration.getPassword());
return result;
}
private JobEventRdbSearch.Condition buildCondition(final UriInfo info, final String[] params) throws ParseException {
int perPage = 10;
int page = 1;
if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("per_page"))) {
perPage = Integer.parseInt(info.getQueryParameters().getFirst("per_page"));
}
if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("page"))) {
page = Integer.parseInt(info.getQueryParameters().getFirst("page"));
}
String sort = info.getQueryParameters().getFirst("sort");
String order = info.getQueryParameters().getFirst("order");
Date startTime = null;
Date endTime = null;
Map<String, Object> fields = getQueryParameters(info, params);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("startTime"))) {
startTime = simpleDateFormat.parse(info.getQueryParameters().getFirst("startTime"));
}
if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst("endTime"))) {
endTime = simpleDateFormat.parse(info.getQueryParameters().getFirst("endTime"));
}
return new JobEventRdbSearch.Condition(perPage, page, sort, order, startTime, endTime, fields);
}
private Map<String, Object> getQueryParameters(final UriInfo info, final String[] params) {
final Map<String, Object> result = new HashMap<>();
for (String each : params) {
if (!Strings.isNullOrEmpty(info.getQueryParameters().getFirst(each))) {
result.put(each, info.getQueryParameters().getFirst(each));
}
}
return result;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.restful;
import io.elasticjob.lite.console.service.JobAPIService;
import io.elasticjob.lite.console.service.impl.JobAPIServiceImpl;
import io.elasticjob.lite.lifecycle.domain.ShardingInfo;
import io.elasticjob.lite.lifecycle.domain.JobBriefInfo;
import com.google.common.base.Optional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Collection;
/**
* 作业维度操作的RESTful API.
*
* @author caohao
*/
@Path("/jobs")
public final class JobOperationRestfulApi {
private JobAPIService jobAPIService = new JobAPIServiceImpl();
/**
* 获取作业总数.
*
* @return 作业总数
*/
@GET
@Path("/count")
public int getJobsTotalCount() {
return jobAPIService.getJobStatisticsAPI().getJobsTotalCount();
}
/**
* 获取作业详情.
*
* @return 作业详情集合
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Collection<JobBriefInfo> getAllJobsBriefInfo() {
return jobAPIService.getJobStatisticsAPI().getAllJobsBriefInfo();
}
/**
* 触发作业.
*
* @param jobName 作业名称
*/
@POST
@Path("/{jobName}/trigger")
public void triggerJob(@PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().trigger(Optional.of(jobName), Optional.<String>absent());
}
/**
* 禁用作业.
*
* @param jobName 作业名称
*/
@POST
@Path("/{jobName}/disable")
@Consumes(MediaType.APPLICATION_JSON)
public void disableJob(@PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().disable(Optional.of(jobName), Optional.<String>absent());
}
/**
* 启用作业.
*
* @param jobName 作业名称
*/
@DELETE
@Path("/{jobName}/disable")
@Consumes(MediaType.APPLICATION_JSON)
public void enableJob(@PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().enable(Optional.of(jobName), Optional.<String>absent());
}
/**
* 终止作业.
*
* @param jobName 作业名称
*/
@POST
@Path("/{jobName}/shutdown")
@Consumes(MediaType.APPLICATION_JSON)
public void shutdownJob(@PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().shutdown(Optional.of(jobName), Optional.<String>absent());
}
/**
* 获取分片信息.
*
* @param jobName 作业名称
* @return 分片信息集合
*/
@GET
@Path("/{jobName}/sharding")
@Produces(MediaType.APPLICATION_JSON)
public Collection<ShardingInfo> getShardingInfo(@PathParam("jobName") final String jobName) {
return jobAPIService.getShardingStatisticsAPI().getShardingInfo(jobName);
}
/**
* Disable Sharding.
*
* @param jobName job name
* @param item sharding item
*/
@POST
@Path("/{jobName}/sharding/{item}/disable")
@Consumes(MediaType.APPLICATION_JSON)
public void disableSharding(@PathParam("jobName") final String jobName, @PathParam("item") final String item) {
jobAPIService.getShardingOperateAPI().disable(jobName, item);
}
/**
* EnableS Sharding.
*
* @param jobName job name
* @param item sharding item
*/
@DELETE
@Path("/{jobName}/sharding/{item}/disable")
@Consumes(MediaType.APPLICATION_JSON)
public void enableSharding(@PathParam("jobName") final String jobName, @PathParam("item") final String item) {
jobAPIService.getShardingOperateAPI().enable(jobName, item);
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.restful;
import io.elasticjob.lite.console.service.JobAPIService;
import io.elasticjob.lite.console.service.impl.JobAPIServiceImpl;
import io.elasticjob.lite.lifecycle.domain.JobBriefInfo;
import io.elasticjob.lite.lifecycle.domain.ServerBriefInfo;
import com.google.common.base.Optional;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Collection;
/**
* 服务器维度操作的RESTful API.
*
* @author caohao
*/
@Path("/servers")
public final class ServerOperationRestfulApi {
private JobAPIService jobAPIService = new JobAPIServiceImpl();
/**
* 获取服务器总数.
*
* @return 服务器总数
*/
@GET
@Path("/count")
public int getServersTotalCount() {
return jobAPIService.getServerStatisticsAPI().getServersTotalCount();
}
/**
* 获取服务器详情.
*
* @return 服务器详情集合
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Collection<ServerBriefInfo> getAllServersBriefInfo() {
return jobAPIService.getServerStatisticsAPI().getAllServersBriefInfo();
}
/**
* 禁用作业.
*
* @param serverIp 服务器IP地址
*/
@POST
@Path("/{serverIp}/disable")
public void disableServer(@PathParam("serverIp") final String serverIp) {
jobAPIService.getJobOperatorAPI().disable(Optional.<String>absent(), Optional.of(serverIp));
}
/**
* 启用作业.
*
* @param serverIp 服务器IP地址
*/
@DELETE
@Path("/{serverIp}/disable")
public void enableServer(@PathParam("serverIp") final String serverIp) {
jobAPIService.getJobOperatorAPI().enable(Optional.<String>absent(), Optional.of(serverIp));
}
/**
* 终止作业.
*
* @param serverIp 服务器IP地址
*/
@POST
@Path("/{serverIp}/shutdown")
public void shutdownServer(@PathParam("serverIp") final String serverIp) {
jobAPIService.getJobOperatorAPI().shutdown(Optional.<String>absent(), Optional.of(serverIp));
}
/**
* 清理作业.
*
* @param serverIp 服务器IP地址
*/
@DELETE
@Path("/{serverIp}")
public void removeServer(@PathParam("serverIp") final String serverIp) {
jobAPIService.getJobOperatorAPI().remove(Optional.<String>absent(), Optional.of(serverIp));
}
/**
* 获取该服务器上注册的作业的简明信息.
*
* @param serverIp 服务器IP地址
* @return 作业简明信息对象集合
*/
@GET
@Path("/{serverIp}/jobs")
@Produces(MediaType.APPLICATION_JSON)
public Collection<JobBriefInfo> getJobs(@PathParam("serverIp") final String serverIp) {
return jobAPIService.getJobStatisticsAPI().getJobsBriefInfo(serverIp);
}
/**
* 禁用作业.
*
* @param serverIp 服务器IP地址
* @param jobName 作业名称
*/
@POST
@Path("/{serverIp}/jobs/{jobName}/disable")
public void disableServerJob(@PathParam("serverIp") final String serverIp, @PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().disable(Optional.of(jobName), Optional.of(serverIp));
}
/**
* 启用作业.
*
* @param serverIp 服务器IP地址
* @param jobName 作业名称
*/
@DELETE
@Path("/{serverIp}/jobs/{jobName}/disable")
public void enableServerJob(@PathParam("serverIp") final String serverIp, @PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().enable(Optional.of(jobName), Optional.of(serverIp));
}
/**
* 终止作业.
*
* @param serverIp 服务器IP地址
* @param jobName 作业名称
*/
@POST
@Path("/{serverIp}/jobs/{jobName}/shutdown")
public void shutdownServerJob(@PathParam("serverIp") final String serverIp, @PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().shutdown(Optional.of(jobName), Optional.of(serverIp));
}
/**
* 清理作业.
*
* @param serverIp 服务器IP地址
* @param jobName 作业名称
*/
@DELETE
@Path("/{serverIp}/jobs/{jobName}")
public void removeServerJob(@PathParam("serverIp") final String serverIp, @PathParam("jobName") final String jobName) {
jobAPIService.getJobOperatorAPI().remove(Optional.of(jobName), Optional.of(serverIp));
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.restful.config;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfiguration;
import io.elasticjob.lite.console.domain.EventTraceDataSourceFactory;
import io.elasticjob.lite.console.service.EventTraceDataSourceConfigurationService;
import io.elasticjob.lite.console.service.impl.EventTraceDataSourceConfigurationServiceImpl;
import io.elasticjob.lite.console.util.SessionEventTraceDataSourceConfiguration;
import com.google.common.base.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.util.Collection;
/**
* 事件追踪数据源配置的RESTful API.
*
* @author caohao
*/
@Path("/data-source")
public final class EventTraceDataSourceRestfulApi {
public static final String DATA_SOURCE_CONFIG_KEY = "data_source_config_key";
private EventTraceDataSourceConfigurationService eventTraceDataSourceConfigurationService = new EventTraceDataSourceConfigurationServiceImpl();
/**
* 判断是否存在已连接的事件追踪数据源配置.
*
* @param request HTTP请求
* @return 是否存在已连接的事件追踪数据源配置
*/
@GET
@Path("/activated")
public boolean activated(final @Context HttpServletRequest request) {
return eventTraceDataSourceConfigurationService.loadActivated().isPresent();
}
/**
* 读取事件追踪数据源配置.
*
* @param request HTTP请求对象
* @return 事件追踪数据源配置集合
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Collection<EventTraceDataSourceConfiguration> load(final @Context HttpServletRequest request) {
Optional<EventTraceDataSourceConfiguration> dataSourceConfig = eventTraceDataSourceConfigurationService.loadActivated();
if (dataSourceConfig.isPresent()) {
setDataSourceNameToSession(dataSourceConfig.get(), request.getSession());
}
return eventTraceDataSourceConfigurationService.loadAll().getEventTraceDataSourceConfiguration();
}
/**
* 添加事件追踪数据源配置.
*
* @param config 事件追踪数据源配置
* @return 是否添加成功
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public boolean add(final EventTraceDataSourceConfiguration config) {
return eventTraceDataSourceConfigurationService.add(config);
}
/**
* 删除事件追踪数据源配置.
*
* @param config 事件追踪数据源配置
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void delete(final EventTraceDataSourceConfiguration config) {
eventTraceDataSourceConfigurationService.delete(config.getName());
}
/**
* 连接事件追踪数据源测试.
*
* @param config 事件追踪数据源配置
* @param request HTTP请求对象
* @return 是否连接成功
*/
@POST
@Path("/connectTest")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public boolean connectTest(final EventTraceDataSourceConfiguration config, final @Context HttpServletRequest request) {
return setDataSourceNameToSession(config, request.getSession());
}
/**
* 连接事件追踪数据源.
*
* @param config 事件追踪数据源配置
* @param request HTTP请求对象
* @return 是否连接成功
*/
@POST
@Path("/connect")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public boolean connect(final EventTraceDataSourceConfiguration config, final @Context HttpServletRequest request) {
boolean isConnected = setDataSourceNameToSession(eventTraceDataSourceConfigurationService.find(config.getName(), eventTraceDataSourceConfigurationService.loadAll()), request.getSession());
if (isConnected) {
eventTraceDataSourceConfigurationService.load(config.getName());
}
return isConnected;
}
private boolean setDataSourceNameToSession(final EventTraceDataSourceConfiguration dataSourceConfig, final HttpSession session) {
session.setAttribute(DATA_SOURCE_CONFIG_KEY, dataSourceConfig);
try {
EventTraceDataSourceFactory.createEventTraceDataSource(dataSourceConfig.getDriver(), dataSourceConfig.getUrl(),
dataSourceConfig.getUsername(), Optional.fromNullable(dataSourceConfig.getPassword()));
SessionEventTraceDataSourceConfiguration.setDataSourceConfiguration((EventTraceDataSourceConfiguration) session.getAttribute(DATA_SOURCE_CONFIG_KEY));
// CHECKSTYLE:OFF
} catch (final Exception ex) {
// CHECKSTYLE:ON
return false;
}
return true;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.restful.config;
import io.elasticjob.lite.console.service.JobAPIService;
import io.elasticjob.lite.console.service.impl.JobAPIServiceImpl;
import io.elasticjob.lite.lifecycle.domain.JobSettings;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* 作业配置的RESTful API.
*
* @author caohao
*/
@Path("/jobs/config")
public final class LiteJobConfigRestfulApi {
private JobAPIService jobAPIService = new JobAPIServiceImpl();
/**
* 获取作业配置.
*
* @param jobName 作业名称
* @return 作业配置
*/
@GET
@Path("/{jobName}")
@Produces(MediaType.APPLICATION_JSON)
public JobSettings getJobSettings(@PathParam("jobName") final String jobName) {
return jobAPIService.getJobSettingsAPI().getJobSettings(jobName);
}
/**
* 修改作业配置.
*
* @param jobSettings 作业配置
*/
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateJobSettings(final JobSettings jobSettings) {
jobAPIService.getJobSettingsAPI().updateJobSettings(jobSettings);
}
/**
* 删除作业配置.
*
* @param jobName 作业名称
*/
@DELETE
@Path("/{jobName}")
public void removeJob(@PathParam("jobName") final String jobName) {
jobAPIService.getJobSettingsAPI().removeJobSettings(jobName);
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.restful.config;
import com.google.common.base.Optional;
import io.elasticjob.lite.console.domain.RegistryCenterConfiguration;
import io.elasticjob.lite.console.service.RegistryCenterConfigurationService;
import io.elasticjob.lite.console.service.impl.RegistryCenterConfigurationServiceImpl;
import io.elasticjob.lite.console.util.SessionRegistryCenterConfiguration;
import io.elasticjob.lite.lifecycle.internal.reg.RegistryCenterFactory;
import io.elasticjob.lite.reg.exception.RegException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import java.util.Collection;
/**
* 注册中心配置的RESTful API.
*
* @author caohao
*/
@Path("/registry-center")
public final class RegistryCenterRestfulApi {
public static final String REG_CENTER_CONFIG_KEY = "reg_center_config_key";
private RegistryCenterConfigurationService regCenterService = new RegistryCenterConfigurationServiceImpl();
/**
* 判断是否存在已连接的注册中心配置.
*
* @param request HTTP请求
* @return 是否存在已连接的注册中心配置
*/
@GET
@Path("/activated")
public boolean activated(final @Context HttpServletRequest request) {
return regCenterService.loadActivated().isPresent();
}
/**
* 读取注册中心配置集合.
*
* @param request HTTP请求
* @return 注册中心配置集合
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
public Collection<RegistryCenterConfiguration> load(final @Context HttpServletRequest request) {
Optional<RegistryCenterConfiguration> regCenterConfig = regCenterService.loadActivated();
if (regCenterConfig.isPresent()) {
setRegistryCenterNameToSession(regCenterConfig.get(), request.getSession());
}
return regCenterService.loadAll().getRegistryCenterConfiguration();
}
/**
* 添加注册中心.
*
* @param config 注册中心配置
* @return 是否添加成功
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
public boolean add(final RegistryCenterConfiguration config) {
return regCenterService.add(config);
}
/**
* 删除注册中心.
*
* @param config 注册中心配置
*/
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
public void delete(final RegistryCenterConfiguration config) {
regCenterService.delete(config.getName());
}
/**
* Connect to registry center.
*
* @param config config of registry center
* @param request http request
* @return true if connected
*/
@POST
@Path("/connect")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public boolean connect(final RegistryCenterConfiguration config, final @Context HttpServletRequest request) {
boolean isConnected = setRegistryCenterNameToSession(regCenterService.find(config.getName(), regCenterService.loadAll()), request.getSession());
if (isConnected) {
regCenterService.load(config.getName());
}
return isConnected;
}
private boolean setRegistryCenterNameToSession(final RegistryCenterConfiguration regCenterConfig, final HttpSession session) {
session.setAttribute(REG_CENTER_CONFIG_KEY, regCenterConfig);
try {
RegistryCenterFactory.createCoordinatorRegistryCenter(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
SessionRegistryCenterConfiguration.setRegistryCenterConfiguration((RegistryCenterConfiguration) session.getAttribute(REG_CENTER_CONFIG_KEY));
} catch (final RegException ex) {
return false;
}
return true;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.service;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfiguration;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfigurations;
import com.google.common.base.Optional;
/**
* 事件追踪数据源配置服务.
*
* @author caohao
*/
public interface EventTraceDataSourceConfigurationService {
/**
* 读取全部事件追踪数据源配置.
*
* @return 全部事件追踪数据源配置
*/
EventTraceDataSourceConfigurations loadAll();
/**
* 读取事件追踪数据源配置.
*
* @param name 配置名称
* @return 事件追踪数据源配置
*/
EventTraceDataSourceConfiguration load(String name);
/**
* 查找事件追踪数据源配置.
*
* @param name 配置名称
* @param configs 全部事件追踪数据源配置
* @return 事件追踪数据源配置
*/
EventTraceDataSourceConfiguration find(String name, EventTraceDataSourceConfigurations configs);
/**
* 读取已连接的事件追踪数据源配置.
*
* @return 已连接的事件追踪数据源配置
*/
Optional<EventTraceDataSourceConfiguration> loadActivated();
/**
* 添加事件追踪数据源配置.
*
* @param config 事件追踪数据源配置
* @return 是否添加成功
*/
boolean add(EventTraceDataSourceConfiguration config);
/**
* 删除事件追踪数据源配置.
*
* @param name 配置名称
*/
void delete(String name);
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.service;
import io.elasticjob.lite.lifecycle.api.JobOperateAPI;
import io.elasticjob.lite.lifecycle.api.JobSettingsAPI;
import io.elasticjob.lite.lifecycle.api.JobStatisticsAPI;
import io.elasticjob.lite.lifecycle.api.ServerStatisticsAPI;
import io.elasticjob.lite.lifecycle.api.ShardingOperateAPI;
import io.elasticjob.lite.lifecycle.api.ShardingStatisticsAPI;
public interface JobAPIService {
/**
* Job settings API.
*
* @return JobSettingsAPI
*/
JobSettingsAPI getJobSettingsAPI();
/**
* Job operate API.
*
* @return JobOperateAPI
*/
JobOperateAPI getJobOperatorAPI();
/**
* Sharding operate API.
*
* @return ShardingOperateAPI
*/
ShardingOperateAPI getShardingOperateAPI();
/**
* Job statistics API.
*
* @return JobStatisticsAPI
*/
JobStatisticsAPI getJobStatisticsAPI();
/**
* Servers statistics API.
*
* @return ServerStatisticsAPI
*/
ServerStatisticsAPI getServerStatisticsAPI();
/**
* Sharding statistics API.
*
* @return ShardingStatisticsAPI
*/
ShardingStatisticsAPI getShardingStatisticsAPI();
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.service;
import io.elasticjob.lite.console.domain.RegistryCenterConfiguration;
import io.elasticjob.lite.console.domain.RegistryCenterConfigurations;
import com.google.common.base.Optional;
/**
* 注册中心配置服务.
*
* @author zhangliang
*/
public interface RegistryCenterConfigurationService {
/**
* 读取全部注册中心配置.
*
* @return 全部注册中心配置
*/
RegistryCenterConfigurations loadAll();
/**
* 读取注册中心配置.
*
* @param name 配置名称
* @return 注册中心配置
*/
RegistryCenterConfiguration load(String name);
/**
* 查找注册中心配置.
*
* @param name 配置名称
* @param configs 全部注册中心配置
* @return 注册中心配置
*/
RegistryCenterConfiguration find(String name, RegistryCenterConfigurations configs);
/**
* 读取已连接的注册中心配置.
*
* @return 已连接的注册中心配置
*/
Optional<RegistryCenterConfiguration> loadActivated();
/**
* 添加注册中心配置.
*
* @param config 注册中心配置
* @return 是否添加成功
*/
boolean add(RegistryCenterConfiguration config);
/**
* 删除注册中心配置.
*
* @param name 配置名称
*/
void delete(String name);
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.service.impl;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfiguration;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfigurations;
import io.elasticjob.lite.console.domain.GlobalConfiguration;
import io.elasticjob.lite.console.repository.ConfigurationsXmlRepository;
import io.elasticjob.lite.console.repository.impl.ConfigurationsXmlRepositoryImpl;
import io.elasticjob.lite.console.service.EventTraceDataSourceConfigurationService;
import com.google.common.base.Optional;
/**
* 事件追踪数据源配置服务实现类.
*
* @author caohao
*/
public final class EventTraceDataSourceConfigurationServiceImpl implements EventTraceDataSourceConfigurationService {
private ConfigurationsXmlRepository configurationsXmlRepository = new ConfigurationsXmlRepositoryImpl();
@Override
public EventTraceDataSourceConfigurations loadAll() {
return loadGlobal().getEventTraceDataSourceConfigurations();
}
@Override
public EventTraceDataSourceConfiguration load(final String name) {
GlobalConfiguration configs = loadGlobal();
EventTraceDataSourceConfiguration result = find(name, configs.getEventTraceDataSourceConfigurations());
setActivated(configs, result);
return result;
}
@Override
public EventTraceDataSourceConfiguration find(final String name, final EventTraceDataSourceConfigurations configs) {
for (EventTraceDataSourceConfiguration each : configs.getEventTraceDataSourceConfiguration()) {
if (name.equals(each.getName())) {
return each;
}
}
return null;
}
private void setActivated(final GlobalConfiguration configs, final EventTraceDataSourceConfiguration toBeConnectedConfig) {
EventTraceDataSourceConfiguration activatedConfig = findActivatedDataSourceConfiguration(configs);
if (!toBeConnectedConfig.equals(activatedConfig)) {
if (null != activatedConfig) {
activatedConfig.setActivated(false);
}
toBeConnectedConfig.setActivated(true);
configurationsXmlRepository.save(configs);
}
}
@Override
public Optional<EventTraceDataSourceConfiguration> loadActivated() {
return Optional.fromNullable(findActivatedDataSourceConfiguration(loadGlobal()));
}
private EventTraceDataSourceConfiguration findActivatedDataSourceConfiguration(final GlobalConfiguration configs) {
for (EventTraceDataSourceConfiguration each : configs.getEventTraceDataSourceConfigurations().getEventTraceDataSourceConfiguration()) {
if (each.isActivated()) {
return each;
}
}
return null;
}
@Override
public boolean add(final EventTraceDataSourceConfiguration config) {
GlobalConfiguration configs = loadGlobal();
boolean result = configs.getEventTraceDataSourceConfigurations().getEventTraceDataSourceConfiguration().add(config);
if (result) {
configurationsXmlRepository.save(configs);
}
return result;
}
@Override
public void delete(final String name) {
GlobalConfiguration configs = loadGlobal();
EventTraceDataSourceConfiguration toBeRemovedConfig = find(name, configs.getEventTraceDataSourceConfigurations());
if (null != toBeRemovedConfig) {
configs.getEventTraceDataSourceConfigurations().getEventTraceDataSourceConfiguration().remove(toBeRemovedConfig);
configurationsXmlRepository.save(configs);
}
}
private GlobalConfiguration loadGlobal() {
GlobalConfiguration result = configurationsXmlRepository.load();
if (null == result.getEventTraceDataSourceConfigurations()) {
result.setEventTraceDataSourceConfigurations(new EventTraceDataSourceConfigurations());
}
return result;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.service.impl;
import io.elasticjob.lite.console.domain.RegistryCenterConfiguration;
import io.elasticjob.lite.console.service.JobAPIService;
import io.elasticjob.lite.console.util.SessionRegistryCenterConfiguration;
import io.elasticjob.lite.lifecycle.api.JobAPIFactory;
import io.elasticjob.lite.lifecycle.api.JobOperateAPI;
import io.elasticjob.lite.lifecycle.api.JobSettingsAPI;
import io.elasticjob.lite.lifecycle.api.JobStatisticsAPI;
import io.elasticjob.lite.lifecycle.api.ServerStatisticsAPI;
import io.elasticjob.lite.lifecycle.api.ShardingOperateAPI;
import io.elasticjob.lite.lifecycle.api.ShardingStatisticsAPI;
import com.google.common.base.Optional;
/**
* 作业API服务实现类.
*
* @author zhangliang
*/
public final class JobAPIServiceImpl implements JobAPIService {
@Override
public JobSettingsAPI getJobSettingsAPI() {
RegistryCenterConfiguration regCenterConfig = SessionRegistryCenterConfiguration.getRegistryCenterConfiguration();
return JobAPIFactory.createJobSettingsAPI(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
}
@Override
public JobOperateAPI getJobOperatorAPI() {
RegistryCenterConfiguration regCenterConfig = SessionRegistryCenterConfiguration.getRegistryCenterConfiguration();
return JobAPIFactory.createJobOperateAPI(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
}
@Override
public ShardingOperateAPI getShardingOperateAPI() {
RegistryCenterConfiguration regCenterConfig = SessionRegistryCenterConfiguration.getRegistryCenterConfiguration();
return JobAPIFactory.createShardingOperateAPI(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
}
@Override
public JobStatisticsAPI getJobStatisticsAPI() {
RegistryCenterConfiguration regCenterConfig = SessionRegistryCenterConfiguration.getRegistryCenterConfiguration();
return JobAPIFactory.createJobStatisticsAPI(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
}
@Override
public ServerStatisticsAPI getServerStatisticsAPI() {
RegistryCenterConfiguration regCenterConfig = SessionRegistryCenterConfiguration.getRegistryCenterConfiguration();
return JobAPIFactory.createServerStatisticsAPI(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
}
@Override
public ShardingStatisticsAPI getShardingStatisticsAPI() {
RegistryCenterConfiguration regCenterConfig = SessionRegistryCenterConfiguration.getRegistryCenterConfiguration();
return JobAPIFactory.createShardingStatisticsAPI(regCenterConfig.getZkAddressList(), regCenterConfig.getNamespace(), Optional.fromNullable(regCenterConfig.getDigest()));
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.service.impl;
import io.elasticjob.lite.console.domain.GlobalConfiguration;
import io.elasticjob.lite.console.domain.RegistryCenterConfiguration;
import io.elasticjob.lite.console.domain.RegistryCenterConfigurations;
import io.elasticjob.lite.console.repository.ConfigurationsXmlRepository;
import io.elasticjob.lite.console.repository.impl.ConfigurationsXmlRepositoryImpl;
import io.elasticjob.lite.console.service.RegistryCenterConfigurationService;
import com.google.common.base.Optional;
/**
* 注册中心配置服务实现类.
*
* @author zhangliang
*/
public final class RegistryCenterConfigurationServiceImpl implements RegistryCenterConfigurationService {
private ConfigurationsXmlRepository configurationsXmlRepository = new ConfigurationsXmlRepositoryImpl();
@Override
public RegistryCenterConfigurations loadAll() {
return loadGlobal().getRegistryCenterConfigurations();
}
@Override
public RegistryCenterConfiguration load(final String name) {
GlobalConfiguration configs = loadGlobal();
RegistryCenterConfiguration result = find(name, configs.getRegistryCenterConfigurations());
setActivated(configs, result);
return result;
}
@Override
public RegistryCenterConfiguration find(final String name, final RegistryCenterConfigurations configs) {
for (RegistryCenterConfiguration each : configs.getRegistryCenterConfiguration()) {
if (name.equals(each.getName())) {
return each;
}
}
return null;
}
private void setActivated(final GlobalConfiguration configs, final RegistryCenterConfiguration toBeConnectedConfig) {
RegistryCenterConfiguration activatedConfig = findActivatedRegistryCenterConfiguration(configs);
if (!toBeConnectedConfig.equals(activatedConfig)) {
if (null != activatedConfig) {
activatedConfig.setActivated(false);
}
toBeConnectedConfig.setActivated(true);
configurationsXmlRepository.save(configs);
}
}
@Override
public Optional<RegistryCenterConfiguration> loadActivated() {
return Optional.fromNullable(findActivatedRegistryCenterConfiguration(loadGlobal()));
}
private RegistryCenterConfiguration findActivatedRegistryCenterConfiguration(final GlobalConfiguration configs) {
for (RegistryCenterConfiguration each : configs.getRegistryCenterConfigurations().getRegistryCenterConfiguration()) {
if (each.isActivated()) {
return each;
}
}
return null;
}
@Override
public boolean add(final RegistryCenterConfiguration config) {
GlobalConfiguration configs = loadGlobal();
boolean result = configs.getRegistryCenterConfigurations().getRegistryCenterConfiguration().add(config);
if (result) {
configurationsXmlRepository.save(configs);
}
return result;
}
@Override
public void delete(final String name) {
GlobalConfiguration configs = loadGlobal();
RegistryCenterConfiguration toBeRemovedConfig = find(name, configs.getRegistryCenterConfigurations());
if (null != toBeRemovedConfig) {
configs.getRegistryCenterConfigurations().getRegistryCenterConfiguration().remove(toBeRemovedConfig);
configurationsXmlRepository.save(configs);
}
}
private GlobalConfiguration loadGlobal() {
GlobalConfiguration result = configurationsXmlRepository.load();
if (null == result.getRegistryCenterConfigurations()) {
result.setRegistryCenterConfigurations(new RegistryCenterConfigurations());
}
return result;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.util;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.File;
/**
* 用户目录工具类.
*
* @author zhangliang
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class HomeFolderUtils {
private static final String USER_HOME = System.getProperty("user.home");
private static final String FILE_SEPARATOR = System.getProperty("file.separator");
private static final String CONSOLE_ROOT_FOLDER = ".elastic-job-console";
/**
* 获取用户目录文件.
*
* @param fileName 文件名
* @return 用户目录所在的文件名
*/
public static String getFilePathInHomeFolder(final String fileName) {
return String.format("%s%s", getHomeFolder(), fileName);
}
/**
* 创建用户目录.
*/
public static void createHomeFolderIfNotExisted() {
File file = new File(getHomeFolder());
if (!file.exists()) {
file.mkdirs();
}
}
private static String getHomeFolder() {
return String.format("%s%s%s%s", USER_HOME, FILE_SEPARATOR, CONSOLE_ROOT_FOLDER, FILE_SEPARATOR);
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.util;
import io.elasticjob.lite.console.domain.EventTraceDataSourceConfiguration;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* 事件追踪数据源配置的会话声明周期.
*
* @author zhangliang
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SessionEventTraceDataSourceConfiguration {
private static EventTraceDataSourceConfiguration eventTraceDataSourceConfiguration;
/**
* 从当前会话范围获取事件追踪数据源配置.
*
* @return 事件追踪数据源配置
*/
public static EventTraceDataSourceConfiguration getEventTraceDataSourceConfiguration() {
return eventTraceDataSourceConfiguration;
}
/**
* 设置事件追踪数据源配置至当前会话范围.
*
* @param eventTraceDataSourceConfiguration 事件追踪数据源配置
*/
public static void setDataSourceConfiguration(final EventTraceDataSourceConfiguration eventTraceDataSourceConfiguration) {
SessionEventTraceDataSourceConfiguration.eventTraceDataSourceConfiguration = eventTraceDataSourceConfiguration;
}
}
/*
* Copyright 1999-2015 dangdang.com.
* <p>
* 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.
* </p>
*/
package io.elasticjob.lite.console.util;
import io.elasticjob.lite.console.domain.RegistryCenterConfiguration;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* 注册中心配置的会话声明周期.
*
* @author zhangliang
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SessionRegistryCenterConfiguration {
private static RegistryCenterConfiguration regCenterConfig;
/**
* 从当前会话范围获取注册中心配置.
*
* @return 事件追踪数据源配置
*/
public static RegistryCenterConfiguration getRegistryCenterConfiguration() {
return regCenterConfig;
}
/**
* 设置注册中心配置至当前会话范围.
*
* @param regCenterConfig 注册中心配置
*/
public static void setRegistryCenterConfiguration(final RegistryCenterConfiguration regCenterConfig) {
SessionRegistryCenterConfiguration.regCenterConfig = regCenterConfig;
}
}
<assembly>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>src/main/resources/conf</directory>
<outputDirectory>conf</outputDirectory>
<directoryMode>0755</directoryMode>
<fileMode>0644</fileMode>
</fileSet>
<fileSet>
<directory>src/main/resources/bin</directory>
<outputDirectory>bin</outputDirectory>
<directoryMode>0755</directoryMode>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
<directoryMode>0755</directoryMode>
</dependencySet>
</dependencySets>
</assembly>
@echo off
if ""%1"" == ""-p"" goto doSetPort
if ""%1"" == """" goto doStart
echo Usage: %0 [OPTIONS]
echo -p [port] Server port (default: 8899)
goto end
:doSetPort
shift
set PORT=%1
:doStart
set CFG_DIR=%~dp0%..
set CLASSPATH=%CFG_DIR%
set CLASSPATH=%~dp0..\lib\*;%CLASSPATH%
set CONSOLE_MAIN=io.elasticjob.lite.console.ConsoleBootstrap
echo on
if ""%PORT%"" == """" set PORT=8899
java -cp "%CLASSPATH%" %CONSOLE_MAIN% %PORT%
:end
#!/bin/bash
show_usage() {
echo "Usage: $0 [OPTIONS]"
echo " -p <port> Server port (default: 8899)"
exit 1
}
if [ $# -ne 0 ] && [ $# -ne 2 ]; then
show_usage
fi
port="8899"
if [ $# -eq 2 ]; then
while getopts p: arg
do case "$arg" in
p) port="$OPTARG";;
[?]) show_usage;;
esac
done
fi
if [ "$port" = "" ]; then
show_usage
fi
cd `dirname $0`
cd ..
DEPLOY_DIR=`pwd`
LIB_DIR=${DEPLOY_DIR}/lib/*
CONSOLE_MAIN=io.elasticjob.lite.console.ConsoleBootstrap
java -classpath ${LIB_DIR}:. ${CONSOLE_MAIN} $port
root.username=root
root.password=root
guest.username=guest
guest.password=guest
.index-content{
min-height:670px;
background-color: #ecf0f5;
z-index: 800;
margin-left: 230px;
}
.form-group.toolbar label {
float: left;
width: 90px;
height: 52px;
padding-left: 20px;
text-align: center;
}
.form-group.toolbar span {
float: left;
width: 40px;
height: 52px;
text-align: center;
}
.update-model-size{
width:210%;
margin-left:-290px;
}
#control-sidebar-theme-demo-options-tab li {
float:left;
width: 33.33333%;
padding: 5px;
}
#control-sidebar-theme-demo-options-tab li a {
display: block;
box-shadow: 0 0 3px rgba(0,0,0,0.4);
}
.top-span {
display:block;
width: 100%;
float: left;
height: 7px;
}
#span-black{
background: #fefefe;
}
.down-span-left {
display:block;
width: 20%;
float: left;
height: 20px;
background: #222d32;
}
.down-span-right {
display:block;
width: 80%;
float: left;
height: 20px;
background: #f4f5f7;
}
#modal-dialog-width{
width: 60%;
}
#execute-result {
float: top;
padding-left: 20px;
}
<div class="index-content">
<section class="content-header">
<h4 data-lang="sidebar-event-trace-data-source"></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-cogs" data-lang="sidebar-global-settings"></i></li>
<li class="active" data-lang="sidebar-event-trace-data-source"></li>
</ol>
</section>
<section class="content">
<table id="data-sources" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true">
<thead>
<tr>
<th data-field="name" data-sortable="true"><span data-lang="event-trace-data-source-name"></span></th>
<th data-field="driver" data-sortable="true"><span data-lang="event-trace-data-source-driver"></span></th>
<th data-field="url" data-sortable="true"><span data-lang="event-trace-data-source-url"></span></th>
<th data-field="username"><span data-lang="event-trace-data-source-username"></span></th>
<th data-field="operation" data-formatter="generateOperationButtons"><span data-lang="operation"></span></th>
</tr>
</thead>
</table>
<button type="button" class="btn-xs btn-success" data-toggle="modal" id="add-data-source" data-lang="operation-add"></button>
</section>
</div>
<div id="add-data-source-center" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" id="close-add-data-source-form" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" data-lang="add-event-trace-data-source"></h4>
</div>
<form id="data-source-form">
<div class="modal-body">
<div class="form-group">
<label for="name" class="control-label" data-lang="event-trace-data-source-name"></label>
<input type="text" class="form-control" id="name" name="name" required autofocus>
</div>
<div class="form-group">
<label for="driver" class="control-label" data-lang="event-trace-data-source-driver"></label>
<select id="driver" name="driver" class="form-control">
MySQL:<option>com.mysql.jdbc.Driver</option>
</select>
</div>
<div class="form-group">
<label for="url" class="control-label" data-lang="event-trace-data-source-url"></label>
<input type="text" class="form-control" id="url" name="url" required>
</div>
<div class="form-group">
<label for="username" class="control-label" data-lang="event-trace-data-source-username"></label>
<input type="text" class="form-control" id="username" name="username" required>
</div>
<div class="form-group">
<label for="password" class="control-label" data-lang="event-trace-data-source-password"></label>
<input type="password" class="form-control" id="password" name="password">
</div>
</div>
<div class="modal-footer">
<button id="connect-test" type="button" class="btn-xs btn-info" data-lang="operation-test-connect"></button>
<button id="add-data-source-btn" type="button" class="btn-xs btn-primary" data-lang="operation-submit"></button>
</div>
</form>
</div>
</div>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
<script src="js/util/common.js"></script>
<script src="js/global/event_trace_data_source.js"></script>
<div class="index-content">
<section class="content-header">
<h4 data-lang="sidebar-registry-center"></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-cogs" data-lang="sidebar-global-settings"></i></li>
<li class="active" data-lang="sidebar-registry-center"></li>
</ol>
</section>
<section class="content">
<table id="reg-centers" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true">
<thead>
<tr>
<th data-field="name" data-sortable="true"><span data-lang="registry-center-name"></span></th>
<th data-field="zkAddressList" data-sortable="true"><span data-lang="registry-center-zk-address"></span></th>
<th data-field="namespace" data-sortable="true"><span data-lang="registry-center-namespace"></span></th>
<th data-field="operation" data-formatter="generateOperationButtons"><span data-lang="operation"></span></th>
</tr>
</thead>
</table>
<button type="button" class="btn-xs btn-success" data-toggle="modal" id="add-register" data-lang="operation-add"></button>
</section>
</div>
<div id="add-reg-center" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" id="close-add-reg-form" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" data-lang="add-registry-center"></h4>
</div>
<form id="reg-center-form">
<div class="modal-body">
<div class="form-group">
<label for="name" class="control-label" data-lang="registry-center-name"></label>
<input type="text" class="form-control" id="name" name="name" required autofocus>
</div>
<div class="form-group">
<label for="zk-address-list" class="control-label" data-lang="registry-center-zk-address"></label>
<input type="text" class="form-control" id="zk-address-list" name="zkAddressList" placeholder="localhost:2181" required>
</div>
<div class="form-group">
<label for="namespace" class="control-label" data-lang="registry-center-namespace"></label>
<input type="text" class="form-control" id="namespace" name="namespace">
</div>
<div class="form-group">
<label for="digest" class="control-label" data-lang="registry-center-digest"></label>
<input type="password" class="form-control" id="digest" name="digest">
</div>
</div>
<div class="modal-footer">
<button id="add-reg-center-btn" type="submit" class="btn-xs btn-primary" data-lang="operation-submit"></button>
</div>
</form>
</div>
</div>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
<script src="js/util/common.js"></script>
<script src="js/global/registry_center.js"></script>
<div class="index-content">
<section class="content-header">
<ol class="breadcrumb">
<li class="active"><i class="fa fa-book" data-lang="sidebar-help"></i></li>
</ol>
</section>
<section class="content">
<h2 data-lang="design-concept-title"></h2>
<ol>
<li data-lang="design-concept-info-1"></li>
<li data-lang="design-concept-info-2"></li>
</ol>
<h2 data-lang="major-features-title"></h2>
<ol>
<li data-lang="major-features-info-1"></li>
<li data-lang="major-features-info-2"></li>
<li data-lang="major-features-info-3"></li>
<li data-lang="major-features-info-4"></li>
<li data-lang="major-features-info-5"></li>
</ol>
<h2 data-lang="unsupported-title"></h2>
<ol>
<li data-lang="unsupported-info"></li>
</ol>
</section>
</div>
<div class="index-content">
<section class="content-header">
<h4 data-lang="sidebar-job-trace"></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-history" data-lang="sidebar-job-history"></i></li>
<li class="active" data-lang="sidebar-job-trace"></li>
</ol>
</section>
<section class="content">
<div id="jobExecDetailToolbar">
<div class="form-inline" role="form">
<div class="form-group toolbar">
<label for="job-name" data-lang="job-name"></label>
<input type="text" class="form-control" id="job-name" placeholder="">
</div>
<div class="form-group toolbar">
<label for="ip" data-lang="server-ip"></label>
<input type="text" class="form-control" data-inputmask="'alias': 'ip'" data-mask="" id="ip">
</div>
<br/>
<div class="form-group toolbar">
<label for="start-time" data-lang="start-time"></label>
<input type="text" class="form-control custom-datepicker" id="start-time">
</div>
<div class="form-group toolbar">
<label for="end-time" data-lang="complete-time"></label>
<input type="text" class="form-control custom-datepicker" id="end-time">
</div>
<div class="form-group">
<div id="execute-result">
<label data-lang="execute-result"></label>:
<input type="radio" name="isSuccess" value="1"><label data-lang="execute-result-success"></label>
<input type="radio" name="isSuccess" value="0"><label data-lang="execute-result-failure"></label>
<input type="radio" name="isSuccess" value="" checked="checked"><label data-lang="execute-result-all"></label>
</div>
</div>
</div>
</div>
<table id="job-exec-details-table"
data-show-refresh="true"
data-show-toggle="true"
data-striped="true"
data-toggle="table"
data-url="/api/event-trace/execution"
data-flat="true"
data-click-to-select="true"
data-row-style="rowStyle"
data-query-params="queryParams"
data-query-params-type="notLimit"
data-side-pagination="server"
data-pagination="true"
data-page-list="[10, 20, 50, 100]"
data-show-columns="true"
data-toolbar="#jobExecDetailToolbar">
<thead>
<tr>
<th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
<th data-field="ip" data-sortable="true"><span data-lang="server-ip"></span></th>
<th data-field="shardingItem"><span data-lang="job-sharding-item"></span></th>
<th data-field="success" data-sortable="true" data-formatter="successFormatter"><span data-lang="execute-result"></span></th>
<th data-field="failureCause.plainText" data-formatter="splitFormatter"><span data-lang="failure-reason"></span></th>
<th data-field="startTime" data-sortable="true" data-formatter="dateTimeFormatter"><span data-lang="start-time"></span></th>
<th data-field="completeTime" data-sortable="true" data-formatter="dateTimeFormatter"><span data-lang="complete-time"></span></th>
</tr>
</thead>
</table>
</section>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="lib/daterangepicker/moment.min.js"></script>
<script src="lib/daterangepicker/daterangepicker.js"></script>
<script src="lib/input-mask/jquery.inputmask.js"></script>
<script src="lib/input-mask/jquery.inputmask.date.extensions.js"></script>
<script src="lib/input-mask/jquery.inputmask.extensions.js"></script>
<script src="js/util/formatter.js"></script>
<script src="js/history/job_event_trace_history.js"></script>
<div class="index-content">
<section class="content-header">
<h4 data-lang="sidebar-history-status"></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-history" data-lang="sidebar-job-history"></i></li>
<li class="active" data-lang="sidebar-history-status"></li>
</ol>
</section>
<section class="content">
<div id="jobExecStatusToolbar">
<div class="form-inline" role="form">
<div class="form-group toolbar">
<label for="job-name" data-lang="job-name"></label>
<input type="text" class="form-control" id="job-name" placeholder="">
</div>
<div class="form-group toolbar">
<label for="state" data-lang="status"></label>
<select id="state" name="state" class="form-control" data-toggle="tooltip" data-placement="bottom" >
<option value="" data-lang="execute-result-all"></option>
<option value="TASK_STAGING" data-lang="status-staging"></option>
<option value="TASK_FAILED" data-lang="status-task-failed"></option>
<option value="TASK_FINISHED" data-lang="status-task-finished"></option>
<option value="TASK_RUNNING" data-lang="status-running"></option>
<option value="TASK_ERROR" data-lang="status-task-error"></option>
<option value="TASK_KILLED" data-lang="status-task-killed"></option>
</select>
</div>
<br/>
<div class="form-group toolbar">
<label for="start-time" data-lang="creation-start-time"></label>
<input type="text" class="form-control pull-right custom-datepicker" id="start-time">
</div>
<div class="form-group toolbar">
<label for="end-time" data-lang="creation-end-time"></label>
<input type="text" class="form-control pull-right custom-datepicker" id="end-time">
</div>
</div>
</div>
<table id="job-exec-status-table"
data-show-refresh="true"
data-show-toggle="true"
data-striped="true"
data-toggle="table"
data-url="/api/event-trace/status"
data-flat="true"
data-click-to-select="true"
data-row-style="rowStyle"
data-query-params="queryParams"
data-query-params-type="notLimit"
data-side-pagination="server"
data-pagination="true"
data-page-list="[10, 20, 50, 100]"
data-show-columns="true"
data-toolbar="#jobExecStatusToolbar">
<thead>
<tr>
<th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
<th data-field="shardingItems"><span data-lang="job-sharding-item"></span></th>
<th data-field="state" data-sortable="true" data-formatter="stateFormatter"><span data-lang="status"></span></th>
<th data-field="creationTime" data-sortable="true" data-formatter="dateTimeFormatter"><span data-lang="creation-time"></span></th>
<th data-field="message" data-formatter="splitRemarkFormatter"><span data-lang="comments"></span></th>
</tr>
</thead>
</table>
</section>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="lib/daterangepicker/moment.min.js"></script>
<script src="lib/daterangepicker/daterangepicker.js"></script>
<script src="lib/input-mask/jquery.inputmask.js"></script>
<script src="lib/input-mask/jquery.inputmask.date.extensions.js"></script>
<script src="lib/input-mask/jquery.inputmask.extensions.js"></script>
<script src="js/util/formatter.js"></script>
<script src="js/history/job_status_history.js"></script>
<div class="index-content">
<section class="content-header">
<h4><span data-lang="job-name"></span>:<span id="job-name"></span></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-tasks" data-lang="sidebar-job-operation"></i></li>
<li><a href="#" id="breadcrumb-job" data-lang="sidebar-job-dimension"></a></li>
<li class="active" data-lang="job-shardings-detail"></li>
</ol>
</section>
<section class="content">
<table id="sharding" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true">
<thead>
<tr>
<th data-field="item" data-sortable="true"><span data-lang="job-sharding-item"></span></th>
<th data-field="serverIp" data-sortable="true"><span data-lang="server-ip"></span></th>
<th data-field="instanceId" data-sortable="true"><span data-lang="job-pid"></span></th>
<th data-field="status" data-formatter="shardingStatusFormatter"><span data-lang="status"></span></th>
<th data-field="failover" data-formatter="failoverFormatter"><span data-lang="job-failover"></span></th>
<th data-field="operation" data-formatter="generateOperationButtons"><span data-lang="operation"></span></th>
</tr>
</thead>
</table>
</section>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="lib/BootstrapValidator/js/bootstrapValidator.js"></script>
<script src="lib/BootstrapValidator/js/bootstrapValidator_zh_CN.js"></script>
<script src="js/util/common.js"></script>
<script src="js/status/job/job_status_detail.js"></script>
<div class="index-content">
<section class="content-header">
<h4 data-lang="sidebar-job-dimension"></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-tasks" data-lang="sidebar-job-operation"></i></li>
<li class="active" data-lang="sidebar-job-dimension"></li>
</ol>
</section>
<section class="content">
<table id="jobs-status-overview-tbl" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true">
<thead>
<tr>
<th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
<th data-field="shardingTotalCount" data-sortable="true"><span data-lang="job-sharding-total-count"></span></th>
<th data-field="cron" data-sortable="true"><span data-lang="job-cron"></span></th>
<th data-field="description" data-sortable="true"><span data-lang="job-description"></span></th>
<th data-field="status" data-sortable="true" data-formatter="statusFormatter"><span data-lang="status"></span></th>
<th data-field="operation" data-formatter="generateOperationButtons"><span data-lang="operation"></span></th>
</tr>
</thead>
</table>
</section>
</div>
<div class="modal" id="data-update-job" tabindex="-1" role="dialog" aria-labelledby="detailModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="update-model-size">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h2 class="modal-title" data-lang="update-job"></h2>
<div id="job-overviews-name" hidden="hidden"></div>
</div>
<div class="modal-body" id="update-job-body">
</div>
</div>
</div>
</div>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="js/util/common.js"></script>
<script src="js/status/job/jobs_status_overview.js"></script>
<div class="index-content">
<section class="content-header">
<h4><span data-lang="server-ip"></span>:<label id="server-ip"></label></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-tasks" data-lang="sidebar-job-operation"></i></li>
<li><a href="#" id="breadcrumb-server" data-lang="sidebar-server-dimension"></a></li>
<li class="active" data-lang="server-detail"></li>
</ol>
</section>
<section class="content">
<table id="server-jobs-tbl" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true">
<thead>
<tr>
<th data-field="jobName" data-sortable="true"><span data-lang="job-name"></span></th>
<th data-field="instanceCount"><span data-lang="server-instance-count"></span></th>
<th data-field="status" data-sortable="true" data-formatter="statusFormatter"><span data-lang="status"></span></th>
<th data-field="operation" data-formatter="generateOperationButtons"><span data-lang="operation"></span></th>
</tr>
</thead>
</table>
<span id="chosen-job-name" class="hide"></span>
</section>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="js/util/common.js"></script>
<script src="js/status/server/server_status_detail.js"></script>
<div class="index-content">
<section class="content-header">
<h4 data-lang="sidebar-server-dimension"></h4>
<ol class="breadcrumb">
<li class="active"><i class="fa fa-tasks" data-lang="sidebar-job-operation"></i></li>
<li class="active" data-lang="sidebar-server-dimension"></li>
</ol>
</section>
<section class="content">
<table id="servers-overview-tbl" data-pagination="true" data-page-list="[10, 20, 50, 100]" data-search="true" data-show-refresh="true" data-show-toggle="true" data-striped="true">
<thead>
<tr>
<th data-field="serverIp" data-sortable="true"><span data-lang="server-ip"></span></th>
<th data-field="instancesNum" data-sortable="true"><span data-lang="server-instance-count"></span></th>
<th data-field="jobsNum" data-sortable="true"><span data-lang="server-job-num"></span></th>
<th data-field="disabledJobsNum" data-sortable="true"><span data-lang="server-disabled-jobs-num"></span></th>
<th data-field="operation" data-formatter="generateOperationButtons"><span data-lang="operation"></span></th>
</tr>
</thead>
</table>
</section>
</div>
<script src="lib/bootstrap-table/bootstrap-table.js"></script>
<script src="js/util/common.js"></script>
<script src="js/status/server/servers_status_overview.js"></script>
sidebar-global-settings=Global settings
sidebar-registry-center=Registry center
sidebar-event-trace-data-source=Event trace data source
sidebar-job-operation=Job operation
sidebar-job-dimension=Job dimension
sidebar-server-dimension=Server dimension
sidebar-job-history=Job history
sidebar-job-trace=Job trace
sidebar-history-status=History status
sidebar-help=Help
switch-language=Switch language
language-zh=中文
language-en=English
dangdang=dangdang.com
switch-theme-title=Switch theme
switch-theme-blue=Blue
switch-theme-white=White
switch-theme-purple=Purple
switch-theme-green=Green
switch-theme-yellow=Yellow
switch-theme-red=Red
switch-theme-blue-light=Blue Light
switch-theme-white-light=White Light
switch-theme-purple-light=Purple Light
switch-theme-green-light=Green Light
switch-theme-yellow-light=Yellow Light
switch-theme-red-light=Red Light
registry-center-dimension=Switch registry center
add-registry-center=Add registry center
registry-center-name=Name
registry-center-zk-address=Zookeeper address
registry-center-namespace=Namespace
registry-center-digest=Digest
registry-center-name-not-null=Registry center name cannot be null
registry-center-name-length-limit=Registry center name should less than 50 characters
registry-center-existed=Registry center already exists
registry-center-zk-address-not-null=Registry center zookeeper address cannot be null
registry-center-zk-address-length-limit=Registry center zookeeper address should less than 100 characters
registry-center-namespace-length-limit=Registry center namespace should less than 50 characters
registry-center-digest-length-limit=Registry center digest should less than 20 characters
registry-center-connect-failed=The operation is unsuccessful, for the reason: the connection failed, please check the registry center configuration
event-trace-data-source-dimension=Switch event trace data source
add-event-trace-data-source=Add event trace data source
event-trace-data-source-name=Name
event-trace-data-source-driver=Driver
event-trace-data-source-url=Url
event-trace-data-source-username=Username
event-trace-data-source-password=Password
event-trace-data-source-name-not-null=Event trace data source name cannot be null
event-trace-data-source-name-length-limit=Event trace data source name should less than 50 characters
event-trace-data-source-existed=Event trace data source already exists
event-trace-data-source-url-not-null=Event trace data source url cannot be null
event-trace-data-source-url-length-limit=Event trace data source url should less than 200 characters
event-trace-data-source-username-not-null=Event trace data source username cannot be null
event-trace-data-source-username-length-limit=Event trace data source username should less than 50 characters
event-trace-data-source-password-length-limit=Event trace data source password should less than 50 characters
event-trace-data-source-test-fail=Event trace data source test connection failed!
event-trace-data-source-test-succeed=Event trace data source test connection succeeded!
event-trace-data-source-connect-failed=The operation is unsuccessful, for the reason: the connection failed, please check the event trace data source configuration
job-detail=Job detail
update-job=Update job
job-name=Job name
job-shardings-detail=Job shardings detail
job-type=Job type
job-class=Job class
job-parameter=Job parameter
job-sharding-strategy-class=Job sharding strategy class
job-exception-handler=Job exception handler
job-cron=Crontab
job-sharding-total-count=Sharding total count
job-max-time-diff-seconds=Max time diff seconds
job-monitor-port=Monitor port
job-reconcile-interval-minutes=Reconcile interval minutes
job-monitor-execution=Monitor execution
job-failover=Failover
job-misfire=Misfire
job-streaming-process=Streaming process
job-sharding-item-parameters=Sharding item parameters
job-executor-service-handler=Executor service handler
job-description=Description
job-script-command-line=Script command line
job-sharding-item=Sharding item
job-pid=PID
job-sharding-count-not-null=Sharding count cannot be null
job-sharding-count-should-be-integer=Sharding count should be integer
job-cron-length-limit=Job crontab should less than 40 characters
job-cron-not-null=Job crontab cannot be null
job-monitor-port-should-be-integer=Job monitor port should be integer
job-monitor-port-not-null=Job monitor port cannot be null
job-monitor-port-range-limit=The monitoring port range must be between 1000 ~ 65535, and -1 indicates that port monitoring is not enabled
placeholder-cron=The cron expression for the job start time
placeholder-sharding-total-count=Sharding total count
placeholder-job-parameter=You can configure multiple identical jobs, but use different parameters as different scheduling instances
placeholder-max-time-diff-seconds=If the time error exceeds the number of configured seconds, the problem will be thrown when the job starts. The configuration is -1 for not checking the time error.
placeholder-monitor-port=Grab the job registration information to listen for the service port. Configuration -1 indicates that the listener service is not enabled.
placeholder-reconcile-interval-minutes=Fix the error-state job server scan repair cycle. The configuration is -1 indicating that the fix action is not enabled.
placeholder-monitor-execution=Each job execution time and interval time is very short, suggest not to monitor homework runtime state in order to enhance efficiency, because it is a transient state, so no need to monitor. Users will be asked to increase their own data accumulation monitoring. There is no guarantee that the data will be repeated, which should be achieved in the operation. It also fails to implement the job failure transfer. It is recommended that the monitoring operation should be monitored at the time of the operation and the interval time.
placeholder-failover=Whether open task execution failure, open said if the job tasks performed in a halfway down, allowing the time unfinished task on the node of another job compensation is carried out.
placeholder-misfire=Whether the task is open misses the reexecution
placeholder-streaming-process=If the data is processed, fetch data will not return empty results and will continue to perform the work. If the data is not flowing, the data is processed and the job is finished
placeholder-sharding-item-parameters=Separate serial Numbers and parameters are separated by equals, and multiple key values are separated by commas, similar to map. The serial serial Numbers start from 0, not greater than or equal to the total number of assignments. For example, 0 = a, 1 = b, 2 = c
placeholder-job-sharding-strategy-class=By default, you can customize the share-chip strategy based on IP address sequentially
placeholder-job-exception-handler=Extension 'JobExceptionHandler' interface, custom exception handling process, the default implementation is log but not throw an exception.
placeholder-executor-service-handler=Extension 'ExecutorServiceHandler' interface, custom thread pool.
placeholder-script-command-line=The full path name of the execution script can contain parameters
server-detail=Server detail
server-ip=Server IP
server-job-num=Job num
server-disabled-jobs-num=Disabled jobs num
server-instance-count=Instance count
operation=Operation
operation-add=Add
operation-submit=Submit
operation-confirm=Confirm
operation-cancel=Cancel
operation-delete=Delete
operation-trigger=Trigger
operation-shutdown=Shutdown
operation-remove=Remove
operation-reset=Reset
operation-test-connect=Test connect
operation-connect=Connect
operation-enable=Enable
operation-disable=Disable
operation-detail=Detail
operation-update=Update
status=Status
status-running=Running
status-sharding-flag=Sharding flag
status-staging=Staging
status-crashed=Crashed
status-ok=OK
status-task-failed=Failed
status-task-finished=Finished
status-task-error=Error
status-task-killed=Killed
status-connected=Connected
status-enabled=Enabled
status-disabled=Disabled
status-offline=Offline
creation-start-time=Creation start time
creation-end-time=Creation end time
creation-time=Creation time
start-time=Start time
complete-time=Complete time
failure-reason=Failure reason
comments=Comments
execute-result=Execute result
execute-result-all=All
execute-result-success=success
execute-result-failure=failure
execute-result-null=Null
design-concept-title=Design concept
design-concept-info-1=Console is not related to Elastic Job, it just reading data from registry center and showing the status of jobs, or updating data to registry center which will change the configuration.
design-concept-info-2=Console can operate lifecycle for jobs, such as enable and disable, but can not the start and stop job's process, because of console server and job servers are completely distributed, console can not control the job servers.
major-features-title=Major features
major-features-info-1=View status of jobs and servers
major-features-info-2=Quick update and delete jobs
major-features-info-3=Disable and enable Jobs
major-features-info-4=Multiple registry centers supported
major-features-info-5=Trace jobs execute history
unsupported-title=Unsupported
unsupported-info=Add job. Because of job is added at first running time automatically, it is unnecessary to add job from console. So just start the job app.
update-job-confirm-info=It will affect the running job by updating these fields are monitor execution, failover and misfire. please be careful!
confirm-to-close=Are you sure to close it?
confirm-to-delete=Are you sure to delete it?
operation-succeed=Operation complete successfully
sidebar-global-settings=全局配置
sidebar-registry-center=注册中心配置
sidebar-event-trace-data-source=事件追踪数据源配置
sidebar-job-operation=作业操作
sidebar-job-dimension=作业维度
sidebar-server-dimension=服务器维度
sidebar-job-history=作业历史
sidebar-job-trace=历史轨迹
sidebar-history-status=历史状态
sidebar-help=帮助
switch-language=选择语言
language-zh=中文
language-en=English
dangdang=当当网
switch-theme-title=更改主题
switch-theme-blue=蓝色
switch-theme-white=白色
switch-theme-purple=紫色
switch-theme-green=绿色
switch-theme-yellow=黄色
switch-theme-red=红色
switch-theme-blue-light=蓝色高亮
switch-theme-white-light=白色高亮
switch-theme-purple-light=紫色高亮
switch-theme-green-light=绿色高亮
switch-theme-yellow-light=黄色高亮
switch-theme-red-light=红色高亮
registry-center-dimension=切换注册中心
add-registry-center=添加注册中心
registry-center-name=注册中心名称
registry-center-zk-address=注册中心地址
registry-center-namespace=命名空间
registry-center-digest=登录凭证
registry-center-name-not-null=注册中心名称不能为空
registry-center-name-length-limit=注册中心名称长度不能超过50字符大小
registry-center-existed=注册中心已经存在
registry-center-zk-address-not-null=注册中心地址不能为空
registry-center-zk-address-length-limit=注册中心地址长度不能超过100字符大小
registry-center-namespace-length-limit=命名空间长度不能超过50字符大小
registry-center-digest-length-limit=登录凭证长度不能超过20字符大小
registry-center-connect-failed=操作未成功,原因:连接失败,请检查注册中心配置
event-trace-data-source-dimension=切换事件追踪数据源
add-event-trace-data-source=添加事件追踪数据源
event-trace-data-source-name=事件追踪数据源名称
event-trace-data-source-driver=事件追踪数据源驱动
event-trace-data-source-url=事件追踪数据源连接地址
event-trace-data-source-username=事件追踪数据源用户名
event-trace-data-source-password=事件追踪数据源密码
event-trace-data-source-name-not-null=数据源名称不能为空
event-trace-data-source-name-length-limit=数据源名称长度不能超过50字符大小
event-trace-data-source-existed=数据源已经存在
event-trace-data-source-url-not-null=数据库URL不能为空
event-trace-data-source-url-length-limit=数据库URL长度不能超过200字符大小
event-trace-data-source-username-not-null=数据库用户名不能为空
event-trace-data-source-username-length-limit=数据库用户名不能超过50字符大小
event-trace-data-source-password-length-limit=数据库口令不能超过50字符大小
event-trace-data-source-test-fail=事件追踪数据源测试连接失败!
event-trace-data-source-test-succeed=事件追踪数据源测试连接成功!
event-trace-data-source-connect-failed=操作未成功,原因:连接失败,请检查事件追踪数据源配置
job-detail=分片状态
update-job=修改作业
job-name=作业名称
job-shardings-detail=作业分片详情
job-type=作业类型
job-class=作业实现类
job-parameter=自定义参数
job-sharding-strategy-class=作业分片策略实现类全路径
job-exception-handler=定制异常处理类全路径
job-cron=Cron表达式
job-sharding-total-count=作业分片总数
job-max-time-diff-seconds=最大容忍本机与注册中心的时间误差秒数
job-monitor-port=监听作业端口
job-reconcile-interval-minutes=作业服务器状态修复周期
job-monitor-execution=监控作业执行时状态
job-failover=支持自动失效转移
job-misfire=支持错过重执行
job-streaming-process=是否流式处理数据
job-sharding-item-parameters=分片序列号/参数对照表
job-executor-service-handler=定制线程池全路径
job-description=作业描述信息
job-script-command-line=脚本作业全路径
job-sharding-item=分片项
job-pid=进程ID
job-sharding-count-not-null=分片数量不能为空
job-sharding-count-should-be-integer=分片数量只能是整数
job-cron-length-limit=cron表达式不能超过40字符大小
job-cron-not-null=cron表达式不能为空
job-monitor-port-should-be-integer=监控端口只能是整数
job-monitor-port-not-null=监控端口不能为空
job-monitor-port-range-limit=监控端口范围必须在1000~65535之间,-1表示不启用端口监控
placeholder-cron=作业启动时间的cron表达式
placeholder-sharding-total-count=作业分片总数
placeholder-job-parameter=可以配置多个相同的作业,但是用不同的参数作为不同的调度实例
placeholder-max-time-diff-seconds=如果时间误差超过配置秒数则作业启动时将抛异常。配置为-1表示不检查时间误差。
placeholder-monitor-port=抓取作业注册信息监听服务端口。配置为-1表示不启用监听服务。
placeholder-reconcile-interval-minutes=修复错误状态的作业服务器扫描修复周期。配置为-1表示不启用修复动作。
placeholder-monitor-execution=每次作业执行时间和间隔时间均非常短的情况,建议不监控作业运行时状态以提升效率,因为是瞬时状态,所以无必要监控。请用户自行增加数据堆积监控。并且不能保证数据重复选取,应在作业中实现幂等性。也无法实现作业失效转移。每次作业执行时间和间隔时间均较长短的情况,建议监控作业运行时状态,可保证数据不会重复选取。
placeholder-failover=是否开启任务执行失效转移,开启表示如果作业在一次任务执行中途宕机,允许将该次未完成的任务在另一作业节点上补偿执行。
placeholder-misfire=是否开启任务错过重新执行
placeholder-streaming-process=如果流式处理数据, 则fetchData不返回空结果将持续执行作业; 如果非流式处理数据, 则处理数据完成后作业结束
placeholder-sharding-item-parameters=分片序列号和参数用等号分隔,多个键值对用逗号分隔,类似map。分片序列号从0开始,不可大于或等于作业分片总数。如:0=a,1=b,2=c
placeholder-job-sharding-strategy-class=默认使用按照IP地址顺序分片策略,可参照文档定制化分片策略
placeholder-job-exception-handler=扩展`JobExceptionHandler`接口,定制异常处理流程,默认实现是记录日志但不抛出异常。
placeholder-executor-service-handler=扩展`ExecutorServiceHandler`接口,定制线程池。
placeholder-script-command-line=执行脚本的全路径名称,可以包含参数
server-detail=服务器详情
server-ip=服务器IP
server-job-num=作业总数
server-disabled-jobs-num=禁用作业数
server-instance-count=运行实例数
operation=操作
operation-add=添加
operation-submit=提交
operation-confirm=确认
operation-cancel=关闭
operation-delete=删除
operation-trigger=触发
operation-shutdown=终止
operation-remove=删除
operation-reset=重置
operation-test-connect=测试连接
operation-connect=连接
operation-enable=生效
operation-disable=失效
operation-detail=详情
operation-update=修改
status=状态
status-running=运行中
status-sharding-flag=分片待调整
status-staging=等待运行
status-crashed=已下线
status-ok=正常
status-task-failed=运行失败
status-task-finished=已完成
status-task-error=启动失败
status-task-killed=主动终止
status-connected=已连
status-enabled=已启用
status-disabled=已失效
status-offline=已下线
creation-start-time=创建开始时间
creation-end-time=创建结束时间
creation-time=创建时间
start-time=开始时间
complete-time=完成时间
failure-reason=失败原因
comments=备注
execute-result=执行结果
execute-result-all=全部
execute-result-success=成功
execute-result-failure=失败
execute-result-null=
design-concept-title=设计理念
design-concept-info-1=本控制台和Elastic Job并无直接关系,是通过读取Elastic Job的注册中心数据展现作业状态,或更新注册中心数据修改全局配置。
design-concept-info-2=控制台只能控制作业本身是否运行,但不能控制作业进程的启停,因为控制台和作业本身服务器是完全分布式的,控制台并不能控制作业服务器。
major-features-title=主要功能
major-features-info-1=查看作业以及服务器状态
major-features-info-2=快捷的修改以及删除作业设置
major-features-info-3=启用和禁用作业
major-features-info-4=跨注册中心查看作业
major-features-info-5=查看作业运行轨迹和运行状态
unsupported-title=不支持项
unsupported-info=添加作业。因为作业都是在首次运行时自动添加,使用控制台添加作业并无必要。直接在作业服务器启动包含Elastic Job的作业进程即可。
update-job-confirm-info=更新监控作业执行时状态、支持自动失效转移、支持misfire会对运行中的作业造成影响,请慎重操作!
confirm-to-close=确认要关闭吗?
confirm-to-delete=确认要删除吗?
operation-succeed=操作已成功完成
$(function() {
doLocale();
authorityControl();
renderDataSources();
validate();
dealDataSourceModal();
handleFieldValidator();
submitDataSource();
bindButtons();
bindConnectionTest();
});
function renderDataSources() {
$("#data-sources").bootstrapTable({
url: "api/data-source",
cache: false,
search: true,
showRefresh: true,
showColumns: true
}).on("all.bs.table", function() {
doLocale();
});
renderDataSourceForDashboardNav();
}
function generateOperationButtons(val, row) {
var operationTd;
var name = row.name;
if (row.activated) {
operationTd = "<button disabled operation='connect-datasource' class='btn-xs' dataSourceName='" + name + "' data-lang='status-connected'></button>&nbsp;<button operation='delete-datasource' class='btn-xs btn-danger' data-toggle='modal' id='delete-dialog' dataSourceName='" + name + "' data-lang='operation-delete'></button>";
} else {
operationTd = "<button operation='connect-datasource' class='btn-xs btn-info' dataSourceName='" + name + "' data-loading-text='loading...' data-lang='operation-connect'></button>&nbsp;<button operation='delete-datasource' class='btn-xs btn-danger' data-toggle='modal' id='delete-dialog' dataSourceName='" + name + "' data-lang='operation-delete'></button>";
}
return operationTd;
}
function bindButtons() {
bindConnectButtons();
bindDeleteButtons();
}
function bindConnectButtons() {
$(document).off("click", "button[operation='connect-datasource']");
$(document).on("click", "button[operation='connect-datasource']", function(event) {
var btn = $(this).button("loading");
var dataSourceName = $(event.currentTarget).attr("dataSourceName");
$.ajax({
url: "api/data-source/connect",
type: "POST",
data: JSON.stringify({"name" : dataSourceName}),
contentType: "application/json",
dataType: "json",
success: function(data) {
if (data) {
$("#data-sources").bootstrapTable("refresh");
renderDataSourceForDashboardNav();
showSuccessDialog();
} else {
showDataSourceFailureDialog();
}
btn.button("reset");
}
});
});
}
function bindDeleteButtons() {
$(document).off("click", "button[operation='delete-datasource']");
$(document).on("click", "button[operation='delete-datasource']", function(event) {
showDeleteConfirmModal();
var dataSourceName = $(event.currentTarget).attr("dataSourceName");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "api/data-source",
type: "DELETE",
data: JSON.stringify({"name" : dataSourceName}),
contentType: "application/json",
dataType: "json",
success: function() {
$("#data-sources").bootstrapTable("refresh");
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
renderDataSourceForDashboardNav();
refreshEventTraceNavTag();
}
});
});
});
}
function dealDataSourceModal() {
$("#add-data-source").click(function() {
$("#add-data-source-center").modal({backdrop: 'static', keyboard: true});
});
$("#close-add-data-source-form").click(function() {
$("#add-data-source-center").on("hide.bs.modal", function () {
$("#data-source-form")[0].reset();
});
$("#data-source-form").data("bootstrapValidator").resetForm();
});
}
function handleFieldValidator() {
$("#password").focus(function() {
$("#data-source-form").data("bootstrapValidator").enableFieldValidators("password", true);
});
$("#password").blur(function() {
$("#data-source-form").data("bootstrapValidator").enableFieldValidators("password", "" === $("#password").val() ? false : true);
});
}
function submitDataSource() {
$("#add-data-source-btn").on("click", function(event) {
if ("" === $("#password").val()) {
$("#data-source-form").data("bootstrapValidator").enableFieldValidators("password", false);
}
var bootstrapValidator = $("#data-source-form").data("bootstrapValidator");
bootstrapValidator.validate();
if(bootstrapValidator.isValid()) {
var name = $("#name").val();
var driver = $("#driver").val();
var url = $("#url").val();
var username = $("#username").val();
var password = $("#password").val();
$.ajax({
url: "api/data-source",
type: "POST",
data: JSON.stringify({"name": name, "driver": driver, "url": url, "username": username, "password": password}),
contentType: "application/json",
dataType: "json",
success: function(data) {
if (data) {
$("#add-data-source-center").on("hide.bs.modal", function() {
$("#data-source-form")[0].reset();
});
$("#data-source-form").data("bootstrapValidator").resetForm();
$("#add-data-source-center").modal("hide");
$("#data-sources").bootstrapTable("refresh");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
renderDataSourceForDashboardNav();
refreshEventTraceNavTag();
}
}
});
}
});
}
function validate() {
$("#data-source-form").bootstrapValidator({
message: "This value is not valid",
feedbackIcons: {
valid: "glyphicon glyphicon-ok",
invalid: "glyphicon glyphicon-remove",
validating: "glyphicon glyphicon-refresh"
},
fields: {
name: {
validators: {
notEmpty: {
message: $.i18n.prop("event-trace-data-source-name-not-null")
},
stringLength: {
max: 50,
message: $.i18n.prop("event-trace-data-source-name-length-limit")
},
callback: {
message: $.i18n.prop("event-trace-data-source-existed"),
callback: function() {
var dataSourceName = $("#name").val();
var result = true;
$.ajax({
url: "api/data-source",
contentType: "application/json",
async: false,
success: function(data) {
for (var index = 0; index < data.length; index++) {
if (dataSourceName === data[index].name) {
result = false;
}
}
}
});
return result;
}
}
}
},
url: {
validators: {
notEmpty: {
message: $.i18n.prop("event-trace-data-source-url-not-null")
},
stringLength: {
max: 200,
message: $.i18n.prop("event-trace-data-source-url-length-limit")
}
}
},
username: {
validators: {
notEmpty: {
message: $.i18n.prop("event-trace-data-source-username-not-null")
},
stringLength: {
max: 50,
message: $.i18n.prop("event-trace-data-source-username-length-limit")
}
}
},
password: {
validators: {
stringLength: {
max: 50,
message: $.i18n.prop("event-trace-data-source-password-length-limit")
}
}
}
}
});
$("#data-source-form").submit(function(event) {
event.preventDefault();
});
}
function bindConnectionTest() {
$("#connect-test").on("click", function() {
var name = $("#name").val();
var driver = $("#driver").val();
var url = $("#url").val();
var username = $("#username").val();
var password = $("#password").val();
$.ajax({
url: "api/data-source/connectTest",
type: "POST",
data: JSON.stringify({"name": name, "driver": driver, "url": url, "username": username, "password": password}),
contentType: "application/json",
dataType: "json",
success: function(data) {
if (data) {
showDataSourceTestConnectionSuccessDialog();
} else {
showDataSourceTestConnectionFailureDialog();
}
}
});
});
}
$(function() {
doLocale();
authorityControl();
renderRegCenters();
validate();
dealRegCenterModal();
handleFieldValidator();
submitRegCenter();
bindButtons();
});
function renderRegCenters() {
$("#reg-centers").bootstrapTable({
url: "api/registry-center",
cache: false,
search: true,
showRefresh: true,
showColumns: true
}).on("all.bs.table", function() {
doLocale();
});
renderRegCenterForDashboardNav();
}
function generateOperationButtons(val, row) {
var operationTd;
var name = row.name;
if (row.activated) {
operationTd = "<button disabled operation='connect-reg-center' class='btn-xs' regName='" + name + "' data-lang='status-connected'></button>&nbsp;<button operation='delete-reg-center' class='btn-xs btn-danger' data-toggle='modal' id='delete-dialog' regName='" + name + "' data-lang='operation-delete'></button>";
} else {
operationTd = "<button operation='connect-reg-center' class='btn-xs btn-info' regName='" + name + "' data-loading-text='loading...' data-lang='operation-connect'></button>&nbsp;<button operation='delete-reg-center' class='btn-xs btn-danger' data-toggle='modal' id='delete-dialog' regName='" + name + "' data-lang='operation-delete'></button>";
}
return operationTd;
}
function bindButtons() {
bindConnectButtons();
bindDeleteButtons();
}
function bindConnectButtons() {
$(document).off("click", "button[operation='connect-reg-center']");
$(document).on("click", "button[operation='connect-reg-center']", function(event) {
var btn = $(this).button("loading");
var regName = $(event.currentTarget).attr("regName");
$.ajax({
url: "api/registry-center/connect",
type: "POST",
data: JSON.stringify({"name" : regName}),
contentType: "application/json",
dataType: "json",
success: function(data) {
if (data) {
$("#reg-centers").bootstrapTable("refresh");
renderRegCenterForDashboardNav();
refreshJobNavTag();
refreshServerNavTag();
showSuccessDialog();
} else {
showRegCenterFailureDialog();
}
btn.button("reset");
}
});
});
}
function bindDeleteButtons() {
$(document).off("click", "button[operation='delete-reg-center']");
$(document).on("click", "button[operation='delete-reg-center']", function(event) {
showDeleteConfirmModal();
var regName = $(event.currentTarget).attr("regName");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "api/registry-center",
type: "DELETE",
data: JSON.stringify({"name" : regName}),
contentType: "application/json",
dataType: "json",
success: function() {
$("#reg-centers").bootstrapTable("refresh");
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
renderRegCenterForDashboardNav();
refreshRegCenterNavTag();
}
});
});
});
}
function dealRegCenterModal() {
$("#add-register").click(function() {
$("#add-reg-center").modal({backdrop: 'static', keyboard: true});
});
$("#close-add-reg-form").click(function() {
$("#add-reg-center").on("hide.bs.modal", function () {
$("#reg-center-form")[0].reset();
});
$("#reg-center-form").data("bootstrapValidator").resetForm();
});
}
function handleFieldValidator() {
$("#digest").focus(function() {
$("#reg-center-form").data("bootstrapValidator").enableFieldValidators("digest", true);
});
$("#digest").blur(function() {
$("#reg-center-form").data("bootstrapValidator").enableFieldValidators("digest", "" !== $("#digest").val());
});
$("#namespace").focus(function() {
$("#reg-center-form").data("bootstrapValidator").enableFieldValidators("namespace", true);
});
$("#namespace").blur(function() {
$("#reg-center-form").data("bootstrapValidator").enableFieldValidators("namespace", "" !== $("#namespace").val());
});
}
function submitRegCenter() {
$("#add-reg-center-btn").on("click", function(event) {
if ("" === $("#digest").val()) {
$("#reg-center-form").data("bootstrapValidator").enableFieldValidators("digest", false);
}
if ("" === $("#namespace").val()) {
$("#reg-center-form").data("bootstrapValidator").enableFieldValidators("namespace", false);
}
var bootstrapValidator = $("#reg-center-form").data("bootstrapValidator");
bootstrapValidator.validate();
if(bootstrapValidator.isValid()) {
var name = $("#name").val();
var zkAddressList = $("#zk-address-list").val();
var namespace = $("#namespace").val();
var digest = $("#digest").val();
$.ajax({
url: "api/registry-center",
type: "POST",
data: JSON.stringify({"name": name, "zkAddressList": zkAddressList, "namespace": namespace, "digest": digest}),
contentType: "application/json",
dataType: "json",
success: function(data) {
if (data) {
$("#add-reg-center").on("hide.bs.modal", function() {
$("#reg-center-form")[0].reset();
});
$("#reg-center-form").data("bootstrapValidator").resetForm();
$("#add-reg-center").modal("hide");
$("#reg-centers").bootstrapTable("refresh");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
renderRegCenterForDashboardNav();
refreshRegCenterNavTag();
}
}
});
}
});
}
function validate() {
$("#reg-center-form").bootstrapValidator({
message: "This value is not valid",
feedbackIcons: {
valid: "glyphicon glyphicon-ok",
invalid: "glyphicon glyphicon-remove",
validating: "glyphicon glyphicon-refresh"
},
fields: {
name: {
validators: {
notEmpty: {
message: $.i18n.prop("registry-center-name-not-null")
},
stringLength: {
max: 50,
message: $.i18n.prop("registry-center-name-length-limit")
},
callback: {
message: $.i18n.prop("registry-center-existed"),
callback: function() {
var regName = $("#name").val();
var result = true;
$.ajax({
url: "api/registry-center",
contentType: "application/json",
async: false,
success: function(data) {
for (var index = 0; index < data.length; index++) {
if (regName === data[index].name) {
result = false;
}
}
}
});
return result;
}
}
}
},
zkAddressList: {
validators: {
notEmpty: {
message: $.i18n.prop("registry-center-zk-address-not-null")
},
stringLength: {
max: 100,
message: $.i18n.prop("registry-center-zk-address-length-limit")
}
}
},
namespace: {
validators: {
stringLength: {
max: 50,
message: $.i18n.prop("registry-center-namespace-length-limit")
}
}
},
digest: {
validators: {
stringLength: {
max: 20,
message: $.i18n.prop("registry-center-digest-length-limit")
}
}
}
}
});
$("#reg-center-form").submit(function(event) {
event.preventDefault();
});
}
$(function() {
$("[data-mask]").inputmask();
$(".toolbar input").bind("keypress", function(event) {
if("13" == event.keyCode) {
$("#job-exec-details-table").bootstrapTable("refresh", {silent: true});
}
});
$("#job-exec-details-table").on("all.bs.table", function() {
doLocale();
});
});
function queryParams(params) {
var sortName = "success" === params.sortName ? "isSuccess" : params.sortName;
return {
per_page: params.pageSize,
page: params.pageNumber,
q: params.searchText,
sort: sortName,
order: params.sortOrder,
jobName: $("#job-name").val(),
startTime: $("#start-time").val(),
endTime: $("#end-time").val(),
ip: $("#ip").val(),
isSuccess: $('input[name = "isSuccess"]:checked ').val()
};
}
function successFormatter(value) {
switch(value)
{
case true:
return "<span class='label label-success' data-lang='execute-result-success'></span>";
case false:
return "<span class='label label-danger' data-lang='execute-result-failure'></span>";
default:
return "<span class='label label-danger' data-lang='execute-result-null'></span>";
}
}
function splitFormatter(value) {
var maxLength = 50;
var replacement = "...";
if(null != value && value.length > maxLength) {
var vauleDetail = value.substring(0 , maxLength - replacement.length) + replacement;
value = value.replace(/\r\n/g,"<br/>").replace(/\n/g,"<br/>").replace(/\'/g, "\\'");
return '<a href="javascript: void(0);" style="color:#FF0000;" onClick="showHistoryMessage(\'' + value + '\')">' + vauleDetail + '</a>';
}
return value;
}
$(function() {
$(".toolbar input").bind("keypress", function(event) {
if("13" == event.keyCode) {
$("#job-exec-status-table").bootstrapTable("refresh", {silent: true});
}
});
$("#job-exec-status-table").on("all.bs.table", function() {
doLocale();
});
});
function queryParams(params) {
var sortName = "success" === params.sortName ? "isSuccess" : params.sortName;
return {
per_page: params.pageSize,
page: params.pageNumber,
q: params.searchText,
sort: sortName,
order: params.sortOrder,
jobName: $("#job-name").val(),
state: $("#state").val(),
startTime: $("#start-time").val(),
endTime: $("#end-time").val()
};
}
function splitRemarkFormatter(value, row) {
var maxLength = 50;
var replacement = "...";
if(null != value && value.length > maxLength) {
var valueDetail = value.substring(0 , maxLength - replacement.length) + replacement;
value = value.replace(/\r\n/g,"<br/>").replace(/\n/g,"<br/>").replace(/\'/g, "\\'");
var remarkHtml;
if ("TASK_FAILED" === row.state || "TASK_ERROR" === row.state) {
remarkHtml = '<a href="javascript: void(0);" style="color:#FF0000;" onClick="showHistoryMessage(\'' + value + '\')">' + valueDetail + '</a>';
} else {
remarkHtml = '<a href="javascript: void(0);" style="color:black;" onClick="showHistoryMessage(\'' + value + '\')">' + valueDetail + '</a>';
}
return remarkHtml;
}
return value;
}
function stateFormatter(value) {
switch(value)
{
case "TASK_STAGING":
return "<span class='label label-default' data-lang='status-staging'></span>";
case "TASK_FAILED":
return "<span class='label label-danger' data-lang='status-task-failed'></span>";
case "TASK_FINISHED":
return "<span class='label label-success' data-lang='status-task-finished'></span>";
case "TASK_RUNNING":
return "<span class='label label-primary' data-lang='status-running'></span>";
case "TASK_ERROR":
return "<span class='label label-danger' data-lang='status-task-error'></span>";
case "TASK_KILLED":
return "<span class='label label-warning' data-lang='status-task-killed'></span>";
default:
return "-";
}
}
$(function() {
$("#content").load("html/global/registry_center.html");
$("#reg-center").click(function() {
$("#content").load("html/global/registry_center.html");
});
$("#event-trace-data-source").click(function() {
$("#content").load("html/global/event_trace_data_source.html");
});
$("#job-status").click(function() {
$("#content").load("html/status/job/jobs_status_overview.html");
});
$("#server-status").click(function() {
$("#content").load("html/status/server/servers_status_overview.html");
});
$("#event-trace-history").click(function() {
$("#content").load("html/history/job_event_trace_history.html");
});
$("#status-history").click(function() {
$("#content").load("html/history/job_status_history.html");
});
$("#help").click(function() {
$("#content").load("html/help/help.html", null, function(){
doLocale();
});
});
switchLanguage();
//初始化显示语言
initLanguage();
});
$(function() {
tooltipLocale();
validate();
bindSubmitJobSettingsForm();
bindResetForm();
});
function tooltipLocale(){
for (var i = 0; i < $("[data-toggle='tooltip']").length; i++) {
var object = $("[data-toggle='tooltip']")[i];
$(object).attr('title',$.i18n.prop("placeholder-" + object.getAttribute("id"))).tooltip('fixTitle');
}
}
function getJobParams() {
var jobName = $("#job-overviews-name").text();
var jobParams;
$.ajax({
url: "/api/jobs/config/" + jobName,
async: false,
success: function(data) {
jobParams = data;
}
});
return jobParams;
}
function bindSubmitJobSettingsForm() {
$("#update-job-info-btn").on("click", function(){
var bootstrapValidator = $("#job-config-form").data("bootstrapValidator");
bootstrapValidator.validate();
if (bootstrapValidator.isValid()) {
var jobName = $("#job-name").val();
var jobType = $("#job-type").val();
var jobClass = $("#job-class").val();
var shardingTotalCount = $("#sharding-total-count").val();
var jobParameter = $("#job-parameter").val();
var cron = $("#cron").val();
var streamingProcess = $("#streaming-process").prop("checked");
var maxTimeDiffSeconds = $("#max-time-diff-seconds").val();
var monitorPort = $("#monitor-port").val();
var monitorExecution = $("#monitor-execution").prop("checked");
var failover = $("#failover").prop("checked");
var misfire = $("#misfire").prop("checked");
var driver = $("#driver").val();
var url = $("#url").val();
var username = $("#username").val();
var password = $("#password").val();
var logLevel = $("#logLevel").val();
var shardingItemParameters = $("#sharding-item-parameters").val();
var jobShardingStrategyClass = $("#job-sharding-strategy-class").val();
var scriptCommandLine = $("#script-command-line").val();
var executorServiceHandler = $("#executor-service-handler").val();
var jobExceptionHandler = $("#job-exception-handler").val();
var description = $("#description").val();
var reconcileIntervalMinutes = $("#reconcile-interval-minutes").val();
var postJson = {jobName: jobName, jobType : jobType, jobClass : jobClass, shardingTotalCount: shardingTotalCount, jobParameter: jobParameter, cron: cron, streamingProcess: streamingProcess, maxTimeDiffSeconds: maxTimeDiffSeconds, monitorPort: monitorPort, monitorExecution: monitorExecution, failover: failover, misfire: misfire, shardingItemParameters: shardingItemParameters, jobShardingStrategyClass: jobShardingStrategyClass, jobProperties: {"executor_service_handler": executorServiceHandler, "job_exception_handler": jobExceptionHandler}, description: description, scriptCommandLine: scriptCommandLine, reconcileIntervalMinutes:reconcileIntervalMinutes};
var jobParams = getJobParams();
if (jobParams.monitorExecution !== monitorExecution || jobParams.failover !== failover || jobParams.misfire !== misfire) {
showUpdateConfirmModal();
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$("#confirm-dialog").modal("hide");
submitAjax(postJson);
});
} else {
submitAjax(postJson);
}
}
});
}
function submitAjax(postJson) {
$.ajax({
url: "/api/jobs/config",
type: "PUT",
data: JSON.stringify(postJson),
contentType: "application/json",
dataType: "json",
success: function() {
$("#data-update-job").modal("hide");
$("#jobs-status-overview-tbl").bootstrapTable("refresh");
showSuccessDialog();
}
});
}
function validate() {
$("#job-config-form").bootstrapValidator({
message: "This value is not valid",
feedbackIcons: {
valid: "glyphicon glyphicon-ok",
invalid: "glyphicon glyphicon-remove",
validating: "glyphicon glyphicon-refresh"
},
fields: {
shardingTotalCount: {
validators: {
notEmpty: {
message: $.i18n.prop("job-sharding-count-not-null")
},
regexp: {
regexp: /^(-?\d+)?$/,
message: $.i18n.prop("job-sharding-count-should-be-integer")
}
}
},
cron: {
validators: {
stringLength: {
max: 40,
message: $.i18n.prop("job-cron-length-limit")
},
notEmpty: {
message: $.i18n.prop("job-cron-not-null")
}
}
},
monitorPort: {
validators: {
regexp: {
regexp: /^(-?\d+)?$/,
message: $.i18n.prop("job-monitor-port-should-be-integer")
},
notEmpty: {
message: $.i18n.prop("job-monitor-port-not-null")
},
callback: {
message: $.i18n.prop("job-monitor-port-range-limit"),
callback: function(value, validator) {
var monitorPort = parseInt(validator.getFieldElements("monitorPort").val(), 10);
if (monitorPort <= 65535) {
validator.updateStatus("monitorPort", "VALID");
return true;
}
return false;
}
}
}
}
}
});
$("#job-config-form").submit(function(event) {
event.preventDefault();
});
}
function bindResetForm() {
$("#reset").click(function() {
$("#job-config-form").data("bootstrapValidator").resetForm();
});
}
$(function() {
$("#job-name").text($("#index-job-name").text());
authorityControl();
renderShardingTable();
renderBreadCrumbMenu();
bindButtons();
});
function renderShardingTable() {
var jobName = $("#job-name").text();
$("#sharding").bootstrapTable({
url: "/api/jobs/" + jobName + "/sharding",
cache: false,
search: true,
showRefresh: true,
showColumns: true
}).on("all.bs.table", function() {
doLocale();
});
}
function shardingStatusFormatter(value, row) {
switch(value) {
case "DISABLED":
return "<span class='label label-warning' data-lang='status-disabled'></span>";
break;
case "RUNNING":
return "<span class='label label-primary' data-lang='status-running'></span>";
break;
case "SHARDING_FLAG":
return "<span class='label label-info' data-lang='' data-lang='status-sharding-flag'></span>";
break;
default:
return "<span class='label label-default' data-lang='status-staging'></span>";
break;
}
}
function failoverFormatter(value, row) {
return value ? "Y" : "-";
}
function generateOperationButtons(val, row) {
var disableButton = "<button operation='disable-sharding' class='btn-xs btn-warning' job-name='" + row.jobName + "' item='" + row.item + "' data-lang='operation-disable'></button>";
var enableButton = "<button operation='enable-sharding' class='btn-xs btn-success' job-name='" + row.jobName + "' item='" + row.item + "' data-lang='operation-enable'></button>";
if ("DISABLED" === row.status) {
return enableButton;
} else {
return disableButton;
}
}
function bindButtons() {
bindDisableButton();
bindEnableButton();
}
function bindDisableButton() {
$(document).off("click", "button[operation='disable-sharding']");
$(document).on("click", "button[operation='disable-sharding']", function(event) {
var jobName = $("#index-job-name").text();
var item = $(event.currentTarget).attr("item");
$.ajax({
url: "/api/jobs/" + jobName + "/sharding/" + item + "/disable",
type: "POST",
success: function() {
showSuccessDialog();
$("#sharding").bootstrapTable("refresh");
}
});
});
}
function bindEnableButton() {
$(document).off("click", "button[operation='enable-sharding']");
$(document).on("click", "button[operation='enable-sharding']", function(event) {
var jobName = $("#index-job-name").text();
var item = $(event.currentTarget).attr("item");
$.ajax({
url: "/api/jobs/" + jobName + "/sharding/" + item + "/disable",
type: "DELETE",
success: function () {
showSuccessDialog();
$("#sharding").bootstrapTable("refresh");
}
});
});
}
function renderBreadCrumbMenu() {
$("#breadcrumb-job").click(function() {
$("#content").load("html/status/job/jobs_status_overview.html", null, function(){
doLocale();
});
});
}
$(function() {
authorityControl();
renderJobsOverview();
bindButtons();
});
function renderJobsOverview() {
var jsonData = {
cache: false,
search: true,
showRefresh: true,
showColumns: true
};
var activated = false;
$.ajax({
url: "/api/registry-center/activated",
async: false,
success: function(data) {
activated = data;
}
});
if (activated) {
jsonData.url = "/api/jobs";
}
$("#jobs-status-overview-tbl").bootstrapTable({
columns: jsonData.columns,
url: jsonData.url,
cache: jsonData.cache
}).on("all.bs.table", function() {
doLocale();
});
}
function statusFormatter(value, row) {
switch(value) {
case "OK":
return "<span class='label label-success' data-lang='status-ok'></span>";
break;
case "DISABLED":
return "<span class='label label-warning' data-lang='status-disabled'></span>";
break;
case "SHARDING_FLAG":
return "<span class='label label-info' data-lang='status-sharding-flag'></span>";
break;
case "CRASHED":
return "<span class='label label-default' data-lang='status-crashed'></span>";
break;
}
}
function generateOperationButtons(val, row) {
var modifyButton = "<button operation='modify-job' class='btn-xs btn-primary' job-name='" + row.jobName + "' data-lang='operation-update'></button>";
var shardingStatusButton = "<button operation='job-detail' class='btn-xs btn-info' job-name='" + row.jobName + "' data-lang='operation-detail'></button>";
var triggerButton = "<button operation='trigger-job' class='btn-xs btn-success' job-name='" + row.jobName + "' data-lang='operation-trigger'></button>";
var disableButton = "<button operation='disable-job' class='btn-xs btn-warning' job-name='" + row.jobName + "' data-lang='operation-disable'></button>";
var enableButton = "<button operation='enable-job' class='btn-xs btn-success' job-name='" + row.jobName + "' data-lang='operation-enable'></button>";
var shutdownButton = "<button operation='shutdown-job' class='btn-xs btn-danger' job-name='" + row.jobName + "' data-lang='operation-shutdown'></button>";
var removeButton = "<button operation='remove-job' class='btn-xs btn-danger' job-name='" + row.jobName + "' data-lang='operation-remove'></button>";
var operationTd = modifyButton + "&nbsp;" + shardingStatusButton + "&nbsp;";
if ("OK" === row.status) {
operationTd = operationTd + triggerButton + "&nbsp;" + disableButton + "&nbsp;" + shutdownButton;
}
if ("DISABLED" === row.status) {
operationTd = operationTd + enableButton + "&nbsp;" + shutdownButton;
}
if ("SHARDING_FLAG" === row.status) {
operationTd = operationTd + "&nbsp;" + shutdownButton;
}
if ("CRASHED" === row.status) {
operationTd = modifyButton + "&nbsp;" + removeButton;
}
return operationTd;
}
function bindButtons() {
bindModifyButton();
bindShardingStatusButton();
bindTriggerButton();
bindShutdownButton();
bindDisableButton();
bindEnableButton();
bindRemoveButton();
}
function bindModifyButton() {
$(document).off("click", "button[operation='modify-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='modify-job'][data-toggle!='modal']", function(event) {
var jobName = $(event.currentTarget).attr("job-name");
$.ajax({
url: "/api/jobs/config/" + jobName,
success: function(data) {
if (null !== data) {
$(".box-body").remove();
$('#update-job-body').load('html/status/job/job_config.html', null, function() {
doLocale();
$('#data-update-job').modal({backdrop : 'static', keyboard : true});
renderJob(data);
$("#job-overviews-name").text(jobName);
});
}
}
});
});
}
function bindShardingStatusButton() {
$(document).off("click", "button[operation='job-detail'][data-toggle!='modal']");
$(document).on("click", "button[operation='job-detail'][data-toggle!='modal']", function(event) {
var jobName = $(event.currentTarget).attr("job-name");
$("#index-job-name").text(jobName);
$("#content").load("html/status/job/job_status_detail.html", null, function(){
doLocale();
});
});
}
function bindTriggerButton() {
$(document).off("click", "button[operation='trigger-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='trigger-job'][data-toggle!='modal']", function(event) {
var jobName = $(event.currentTarget).attr("job-name");
$.ajax({
url: "/api/jobs/" + jobName + "/trigger",
type: "POST",
success: function() {
showSuccessDialog();
$("#jobs-status-overview-tbl").bootstrapTable("refresh");
}
});
});
}
function bindDisableButton() {
$(document).off("click", "button[operation='disable-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='disable-job'][data-toggle!='modal']", function(event) {
var jobName = $(event.currentTarget).attr("job-name");
$.ajax({
url: "/api/jobs/" + jobName + "/disable",
type: "POST",
success: function() {
showSuccessDialog();
$("#jobs-status-overview-tbl").bootstrapTable("refresh");
}
});
});
}
function bindEnableButton() {
$(document).off("click", "button[operation='enable-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='enable-job'][data-toggle!='modal']", function(event) {
var jobName = $(event.currentTarget).attr("job-name");
$.ajax({
url: "/api/jobs/" + jobName + "/disable",
type: "DELETE",
success: function() {
showSuccessDialog();
$("#jobs-status-overview-tbl").bootstrapTable("refresh");
}
});
});
}
function bindShutdownButton() {
$(document).off("click", "button[operation='shutdown-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='shutdown-job'][data-toggle!='modal']", function(event) {
showShutdownConfirmModal();
var jobName = $(event.currentTarget).attr("job-name");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "/api/jobs/" + jobName + "/shutdown",
type: "POST",
success: function () {
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
$("#jobs-status-overview-tbl").bootstrapTable("refresh");
}
});
});
});
}
function bindRemoveButton() {
$(document).off("click", "button[operation='remove-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='remove-job'][data-toggle!='modal']", function(event) {
var jobName = $(event.currentTarget).attr("job-name");
showDeleteConfirmModal();
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "/api/jobs/config/" + jobName,
type: "DELETE",
success: function() {
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
refreshJobNavTag();
refreshServerNavTag();
$("#jobs-status-overview-tbl").bootstrapTable("refresh");
}
});
});
});
}
function renderJob(data) {
$("#job-name").attr("value", data.jobName);
$("#job-type").attr("value", data.jobType);
$("#job-class").attr("value", data.jobClass);
$("#sharding-total-count").attr("value", data.shardingTotalCount);
$("#cron").attr("value", data.cron);
$("#sharding-item-parameters").text(data.shardingItemParameters);
$("#job-parameter").attr("value", data.jobParameter);
$("#monitor-execution").attr("checked", data.monitorExecution);
$("#failover").attr("checked", data.failover);
$("#misfire").attr("checked", data.misfire);
$("#streaming-process").attr("checked", data.streamingProcess);
$("#max-time-diff-seconds").attr("value", data.maxTimeDiffSeconds);
$("#monitor-port").attr("value", data.monitorPort);
$("#job-sharding-strategy-class").attr("value", data.jobShardingStrategyClass);
$("#executor-service-handler").attr("value", data.jobProperties["executor_service_handler"]);
$("#job-exception-handler").attr("value", data.jobProperties["job_exception_handler"]);
$("#reconcile-interval-minutes").attr("value", data.reconcileIntervalMinutes);
$("#description").text(data.description);
$("#script-command-line").attr("value", data.scriptCommandLine);
if ("DATAFLOW" === $("#job-type").val()) {
$("#streaming-process-group").show();
}
if ("SCRIPT" === $("#job-type").val()) {
$("#script-commandLine-group").show();
}
}
$(function() {
$("#server-ip").text($("#index-server-ip").text());
authorityControl();
renderJobs();
renderBreadCrumbMenu();
bindButtons();
});
function renderJobs() {
var ip = $("#server-ip").text();
$("#server-jobs-tbl").bootstrapTable({
url: "/api/servers/" + ip + "/jobs",
cache: false,
search: true,
showRefresh: true,
showColumns: true
}).on("all.bs.table", function() {
doLocale();
});
}
function statusFormatter(val, row) {
if (0 === row.instanceCount ) {
return "<span class='label label-default' data-lang='status-offline'></span>";
}
switch(val) {
case "OK":
return "<span class='label label-success' data-lang='status-enabled'></span>";
break;
case "DISABLED":
return "<span class='label label-warning' data-lang='status-disabled'></span>";
break;
}
}
function generateOperationButtons(val, row) {
if (0 === row.instanceCount ) {
return "<button operation='remove-server-job' class='btn-xs btn-danger' job-name='" + row.jobName + "' data-lang='operation-remove'></button>";
}
var disableButton = "<button operation='disable-server-job' class='btn-xs btn-warning' ip='" + row.ip + "' job-name='" + row.jobName + "' data-lang='operation-disable'></button>";
var enableButton = "<button operation='enable-server-job' class='btn-xs btn-success' ip='" + row.ip + "' job-name='" + row.jobName + "' data-lang='operation-enable'></button>";
var shutdownButton = "<button operation='shutdown-server-job' class='btn-xs btn-danger' job-name='" + row.jobName + "' data-lang='operation-shutdown'></button>";
var operationTd = "";
if ("DISABLED" === row.status) {
operationTd = enableButton + "&nbsp;" + shutdownButton;
} else {
operationTd = disableButton + "&nbsp;" + shutdownButton;
}
return operationTd;
}
function bindButtons() {
bindDisableButton();
bindEnableButton();
bindShutdownButton();
bindRemoveButton();
}
function bindDisableButton() {
$(document).off("click", "button[operation='disable-server-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='disable-server-job'][data-toggle!='modal']", function(event) {
$.ajax({
url: "/api/servers/" + $("#server-ip").text() + "/jobs/" + $(event.currentTarget).attr("job-name") + "/disable",
type: "POST",
success: function() {
$("#server-jobs-tbl").bootstrapTable("refresh");
showSuccessDialog();
}
});
});
}
function bindEnableButton() {
$(document).off("click", "button[operation='enable-server-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='enable-server-job'][data-toggle!='modal']", function(event) {
$.ajax({
url: "/api/servers/" + $("#server-ip").text() + "/jobs/" + $(event.currentTarget).attr("job-name") + "/disable",
type: "DELETE",
success: function() {
$("#server-jobs-tbl").bootstrapTable("refresh");
showSuccessDialog();
}
});
});
}
function bindShutdownButton() {
$(document).off("click", "button[operation='shutdown-server-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='shutdown-server-job'][data-toggle!='modal']", function(event) {
showShutdownConfirmModal();
var serverIp = $("#server-ip").text();
var jobName = $(event.currentTarget).attr("job-name");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "/api/servers/" + serverIp + "/jobs/" + jobName + "/shutdown",
type: "POST",
success: function () {
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
$("#server-jobs-tbl").bootstrapTable("refresh");
}
});
});
});
}
function bindRemoveButton() {
$(document).off("click", "button[operation='remove-server-job'][data-toggle!='modal']");
$(document).on("click", "button[operation='remove-server-job'][data-toggle!='modal']", function(event) {
showDeleteConfirmModal();
var serverIp = $("#server-ip").text();
var jobName = $(event.currentTarget).attr("job-name");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "/api/servers/" + serverIp + "/jobs/" + jobName,
type: "DELETE",
success: function () {
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
refreshServerNavTag();
$("#server-jobs-tbl").bootstrapTable("refresh");
}
});
});
});
}
function renderBreadCrumbMenu() {
$("#breadcrumb-server").click(function() {
$("#content").load("html/status/server/servers_status_overview.html", null, function(){
doLocale();
});
});
}
$(function() {
authorityControl();
renderServersOverview();
bindButtons();
});
function renderServersOverview() {
var jsonData = {
cache: false,
search: true,
showRefresh: true,
showColumns: true
};
var activated = false;
$.ajax({
url: "/api/registry-center/activated",
async: false,
success: function(data) {
activated = data;
}
});
if (activated) {
jsonData.url = "/api/servers";
}
$("#servers-overview-tbl").bootstrapTable({
columns: jsonData.columns,
url: jsonData.url,
cache: jsonData.cache
}).on("all.bs.table", function() {
doLocale();
});
}
function bindButtons() {
bindServerStatusDetailButton();
bindDisableServerButton();
bindEnableServerButton();
bindShutdownServerButton();
bindRemoveServerButton();
}
function generateOperationButtons(val, row) {
var detailButton = "<button operation='server-detail' class='btn-xs btn-info' server-ip='" + row.serverIp + "' data-lang='operation-detail'></button>";
var disableButton = "<button operation='disable-server' class='btn-xs btn-warning' server-ip='" + row.serverIp + "' data-lang='operation-disable'></button>";
var enableButton = "<button operation='enable-server' class='btn-xs btn-success' server-ip='" + row.serverIp + "' data-lang='operation-enable'></button>";
var shutdownButton = "<button operation='shutdown-server' class='btn-xs btn-danger' server-ip='" + row.serverIp + "' data-lang='operation-shutdown'></button>";
var removeButton = "<button operation='remove-server' class='btn-xs btn-danger' server-ip='" + row.serverIp + "' data-lang='operation-remove'></button>";
if (row.instancesNum == 0) {
return removeButton;
}
var operationTd = "";
if (row.disabledJobsNum > 0) {
operationTd = detailButton + "&nbsp;" + enableButton + "&nbsp;" + shutdownButton;
} else if (row.instancesNum > 0) {
operationTd = detailButton + "&nbsp;" + disableButton + "&nbsp;" + shutdownButton;
}
return operationTd;
}
function bindServerStatusDetailButton() {
$(document).off("click", "button[operation='server-detail'][data-toggle!='modal']");
$(document).on("click", "button[operation='server-detail'][data-toggle!='modal']", function(event) {
var serverIp = $(event.currentTarget).attr("server-ip");
$("#index-server-ip").text(serverIp);
$("#content").load("html/status/server/server_status_detail.html", null, function(){
doLocale();
});
});
}
function bindDisableServerButton() {
$(document).off("click", "button[operation='disable-server'][data-toggle!='modal']");
$(document).on("click", "button[operation='disable-server'][data-toggle!='modal']", function(event) {
var serverIp = $(event.currentTarget).attr("server-ip");
$.ajax({
url: "/api/servers/" + serverIp + "/disable",
type: "POST",
success: function() {
showSuccessDialog();
$("#servers-overview-tbl").bootstrapTable("refresh");
}
});
});
}
function bindEnableServerButton() {
$(document).off("click", "button[operation='enable-server'][data-toggle!='modal']");
$(document).on("click", "button[operation='enable-server'][data-toggle!='modal']", function(event) {
var serverIp = $(event.currentTarget).attr("server-ip");
$.ajax({
url: "/api/servers/" + serverIp + "/disable",
type: "DELETE",
success: function() {
showSuccessDialog();
$("#servers-overview-tbl").bootstrapTable("refresh");
}
});
});
}
function bindShutdownServerButton() {
$(document).off("click", "button[operation='shutdown-server'][data-toggle!='modal']");
$(document).on("click", "button[operation='shutdown-server'][data-toggle!='modal']", function(event) {
showShutdownConfirmModal();
var serverIp = $(event.currentTarget).attr("server-ip");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "/api/servers/" + serverIp + "/shutdown",
type: "POST",
success: function () {
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
$("#servers-overview-tbl").bootstrapTable("refresh");
}
});
});
});
}
function bindRemoveServerButton() {
$(document).off("click", "button[operation='remove-server'][data-toggle!='modal']");
$(document).on("click", "button[operation='remove-server'][data-toggle!='modal']", function(event) {
showDeleteConfirmModal();
var serverIp = $(event.currentTarget).attr("server-ip");
$(document).off("click", "#confirm-btn");
$(document).on("click", "#confirm-btn", function() {
$.ajax({
url: "/api/servers/" + serverIp,
type: "DELETE",
success: function () {
$("#confirm-dialog").modal("hide");
$(".modal-backdrop").remove();
$("body").removeClass("modal-open");
refreshServerNavTag();
$("#servers-overview-tbl").bootstrapTable("refresh");
}
});
});
});
}
$(function() {
$("[data-toggle='tooltip']").tooltip();
});
function showDialog(msg, timeout) {
$("#message-info").text(msg);
$("#message-dialog").modal("show");
if(null !== timeout) {
setTimeout('$("#message-dialog").modal("hide")', timeout);
}
}
function showSuccessDialog() {
showInfoDialog($.i18n.prop("operation-succeed"));
}
function showInfoDialog(msg) {
showDialog(msg, 2000);
}
function showFailureDialog(msg) {
showDialog(msg, null);
}
function authorityControl() {
$.ajax({
type: "HEAD",
url : "/",
complete: function(xhr, data) {
if ("guest" === xhr.getResponseHeader("identify")) {
$("table").on("all.bs.table", function() {
$(".index-content .btn-xs").not(".btn-info").attr("disabled", true);
$(".index-content .btn-xs").not(".btn-info").removeClass().addClass("btn-xs");
});
}
if ("" === $("#authority").text()) {
$("#authority").text(xhr.getResponseHeader("identify"));
}
}
});
}
function showDeleteConfirmModal() {
$("#confirm-info").text($.i18n.prop("confirm-to-delete"));
$("#confirm-dialog").modal({backdrop: 'static', keyboard: true});
}
function showShutdownConfirmModal() {
$("#confirm-info").text($.i18n.prop("confirm-to-close"));
$("#confirm-dialog").modal({backdrop: 'static', keyboard: true});
}
function showUpdateConfirmModal() {
$("#confirm-info").text($.i18n.prop("update-job-confirm-info"));
$("#confirm-dialog").modal({backdrop: 'static', keyboard: true});
}
function showDataSourceFailureDialog() {
showFailureDialog($.i18n.prop("event-trace-data-source-connect-failed"));
}
function showRegCenterFailureDialog() {
showFailureDialog($.i18n.prop("registry-center-connect-failed"));
}
function showDataSourceTestConnectionSuccessDialog() {
showInfoDialog($.i18n.prop("event-trace-data-source-test-succeed"));
}
function showDataSourceTestConnectionFailureDialog() {
showInfoDialog($.i18n.prop("event-trace-data-source-test-fail"));
}
function i18n(lang) {
jQuery.i18n.properties({
name : 'message',
path : '/i18n/',
mode : 'map',
language : lang,
cache: true,
encoding: 'UTF-8',
callback : function() {
for (var i in $.i18n.map) {
$('[data-lang="'+i+'"]').html($.i18n.prop(i));
}
}
});
}
function doLocale() {
if ($("#content").hasClass("lang-en")) {
i18n("en");
} else {
i18n("zh");
}
}
function switchLanguage() {
$("#lang-zh").click(function() {
$("#content").removeClass("lang-en").addClass("lang-zh");
doLocale();
});
$("#lang-en").click(function() {
$("#content").removeClass("lang-zh").addClass("lang-en");
doLocale();
});
}
/**
* 根据浏览器语言初始化显示语言
*/
function initLanguage() {
//获取浏览器语言
var lan = (navigator.language || navigator.browserLanguage);
if (lan && lan.toLowerCase().indexOf('zh') > -1) {
//切换成中文
$("#lang-zh").click();
}
}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment