mirror of
https://github.com/apache/rocketmq-dashboard.git
synced 2026-02-20 20:35:43 +08:00
Remove the 'ng' suffix of sub projects.
This commit is contained in:
6
src/main/docker/Dockerfile
Normal file
6
src/main/docker/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM java:8
|
||||
VOLUME /tmp
|
||||
ADD rocketmq-console-ng-1.0.0.jar app.jar
|
||||
RUN sh -c 'touch /app.jar'
|
||||
ENV JAVA_OPTS=""
|
||||
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -jar /app.jar" ]
|
||||
31
src/main/java/org/apache/rocketmq/console/App.java
Normal file
31
src/main/java/org/apache/rocketmq/console/App.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@EnableAutoConfiguration
|
||||
@SpringBootApplication
|
||||
public class App {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(App.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.aspect.admin;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
|
||||
import org.apache.rocketmq.console.service.client.MQAdminInstance;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Aspect
|
||||
@Service
|
||||
public class MQAdminAspect {
|
||||
private Logger logger = LoggerFactory.getLogger(MQAdminAspect.class);
|
||||
|
||||
public MQAdminAspect() {
|
||||
}
|
||||
|
||||
@Pointcut("execution(* org.apache.rocketmq.console.service.client.MQAdminExtImpl..*(..))")
|
||||
public void mQAdminMethodPointCut() {
|
||||
|
||||
}
|
||||
|
||||
@Pointcut("@annotation(org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod)")
|
||||
public void multiMQAdminMethodPointCut() {
|
||||
|
||||
}
|
||||
|
||||
@Around(value = "mQAdminMethodPointCut() || multiMQAdminMethodPointCut()")
|
||||
public Object aroundMQAdminMethod(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
long start = System.currentTimeMillis();
|
||||
Object obj = null;
|
||||
try {
|
||||
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
MultiMQAdminCmdMethod multiMQAdminCmdMethod = method.getAnnotation(MultiMQAdminCmdMethod.class);
|
||||
if (multiMQAdminCmdMethod != null && multiMQAdminCmdMethod.timeoutMillis() > 0) {
|
||||
MQAdminInstance.initMQAdminInstance(multiMQAdminCmdMethod.timeoutMillis());
|
||||
}
|
||||
else {
|
||||
MQAdminInstance.initMQAdminInstance(0);
|
||||
}
|
||||
obj = joinPoint.proceed();
|
||||
}
|
||||
finally {
|
||||
MQAdminInstance.destroyMQAdminInstance();
|
||||
logger.debug("op=look method={} cost={}", joinPoint.getSignature().getName(), System.currentTimeMillis() - start);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.aspect.admin.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface MultiMQAdminCmdMethod {
|
||||
long timeoutMillis() default 0;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.aspect.admin.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface OriginalControllerReturnValue {
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.config;
|
||||
|
||||
import java.io.File;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import static org.apache.rocketmq.client.ClientConfig.SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "rocketmq.config")
|
||||
public class RMQConfigure {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(RMQConfigure.class);
|
||||
//use rocketmq.namesrv.addr first,if it is empty,than use system proerty or system env
|
||||
private volatile String namesrvAddr = System.getProperty(MixAll.NAMESRV_ADDR_PROPERTY, System.getenv(MixAll.NAMESRV_ADDR_ENV));
|
||||
|
||||
private volatile String isVIPChannel = System.getProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, "true");
|
||||
|
||||
|
||||
private String dataPath;
|
||||
|
||||
private boolean enableDashBoardCollect;
|
||||
|
||||
public String getNamesrvAddr() {
|
||||
return namesrvAddr;
|
||||
}
|
||||
|
||||
public void setNamesrvAddr(String namesrvAddr) {
|
||||
if (StringUtils.isNotBlank(namesrvAddr)) {
|
||||
this.namesrvAddr = namesrvAddr;
|
||||
System.setProperty(MixAll.NAMESRV_ADDR_PROPERTY, namesrvAddr);
|
||||
logger.info("setNameSrvAddrByProperty nameSrvAddr={}", namesrvAddr);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRocketMqConsoleDataPath() {
|
||||
return dataPath;
|
||||
}
|
||||
|
||||
public String getConsoleCollectData() {
|
||||
return dataPath + File.separator + "dashboard";
|
||||
}
|
||||
|
||||
public void setDataPath(String dataPath) {
|
||||
this.dataPath = dataPath;
|
||||
}
|
||||
|
||||
public String getIsVIPChannel() {
|
||||
return isVIPChannel;
|
||||
}
|
||||
|
||||
public void setIsVIPChannel(String isVIPChannel) {
|
||||
if (StringUtils.isNotBlank(isVIPChannel)) {
|
||||
this.isVIPChannel = isVIPChannel;
|
||||
System.setProperty(SEND_MESSAGE_WITH_VIP_CHANNEL_PROPERTY, isVIPChannel);
|
||||
logger.info("setIsVIPChannel isVIPChannel={}", isVIPChannel);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnableDashBoardCollect() {
|
||||
return enableDashBoardCollect;
|
||||
}
|
||||
|
||||
public void setEnableDashBoardCollect(String enableDashBoardCollect) {
|
||||
this.enableDashBoardCollect = Boolean.valueOf(enableDashBoardCollect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import org.apache.rocketmq.console.service.ClusterService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/cluster")
|
||||
public class ClusterController {
|
||||
|
||||
@Resource
|
||||
private ClusterService clusterService;
|
||||
|
||||
@RequestMapping(value = "/list.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object list() {
|
||||
return clusterService.list();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/brokerConfig.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object brokerConfig(@RequestParam String brokerAddr) {
|
||||
return clusterService.getBrokerConfig(brokerAddr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
|
||||
import org.apache.rocketmq.console.model.ConnectionInfo;
|
||||
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
|
||||
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
|
||||
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
|
||||
import org.apache.rocketmq.console.service.ConsumerService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/consumer")
|
||||
public class ConsumerController {
|
||||
private Logger logger = LoggerFactory.getLogger(ConsumerController.class);
|
||||
|
||||
@Resource
|
||||
private ConsumerService consumerService;
|
||||
|
||||
@RequestMapping(value = "/groupList.query")
|
||||
@ResponseBody
|
||||
public Object list() {
|
||||
return consumerService.queryGroupList();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/group.query")
|
||||
@ResponseBody
|
||||
public Object groupQuery(@RequestParam String consumerGroup) {
|
||||
return consumerService.queryGroup(consumerGroup);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/resetOffset.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object resetOffset(@RequestBody ResetOffsetRequest resetOffsetRequest) {
|
||||
logger.info("op=look resetOffsetRequest={}", JsonUtil.obj2String(resetOffsetRequest));
|
||||
return consumerService.resetOffset(resetOffsetRequest);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/examineSubscriptionGroupConfig.query")
|
||||
@ResponseBody
|
||||
public Object examineSubscriptionGroupConfig(@RequestParam String consumerGroup) {
|
||||
return consumerService.examineSubscriptionGroupConfig(consumerGroup);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/deleteSubGroup.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object deleteSubGroup(@RequestBody DeleteSubGroupRequest deleteSubGroupRequest) {
|
||||
return consumerService.deleteSubGroup(deleteSubGroupRequest);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/createOrUpdate.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object consumerCreateOrUpdateRequest(@RequestBody ConsumerConfigInfo consumerConfigInfo) {
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(consumerConfigInfo.getBrokerNameList()) || CollectionUtils.isNotEmpty(consumerConfigInfo.getClusterNameList()),
|
||||
"clusterName or brokerName can not be all blank");
|
||||
return consumerService.createAndUpdateSubscriptionGroupConfig(consumerConfigInfo);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/fetchBrokerNameList.query", method = {RequestMethod.GET})
|
||||
@ResponseBody
|
||||
public Object fetchBrokerNameList(@RequestParam String consumerGroup) {
|
||||
return consumerService.fetchBrokerNameSetBySubscriptionGroup(consumerGroup);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/queryTopicByConsumer.query")
|
||||
@ResponseBody
|
||||
public Object queryConsumerByTopic(@RequestParam String consumerGroup) {
|
||||
return consumerService.queryConsumeStatsListByGroupName(consumerGroup);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/consumerConnection.query")
|
||||
@ResponseBody
|
||||
public Object consumerConnection(@RequestParam(required = false) String consumerGroup) {
|
||||
ConsumerConnection consumerConnection = consumerService.getConsumerConnection(consumerGroup);
|
||||
consumerConnection.setConnectionSet(ConnectionInfo.buildConnectionInfoHashSet(consumerConnection.getConnectionSet()));
|
||||
return consumerConnection;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/consumerRunningInfo.query")
|
||||
@ResponseBody
|
||||
public Object getConsumerRunningInfo(@RequestParam String consumerGroup, @RequestParam String clientId,
|
||||
@RequestParam boolean jstack) {
|
||||
return consumerService.getConsumerRunningInfo(consumerGroup, clientId, jstack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.apache.rocketmq.console.service.DashboardService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/dashboard")
|
||||
public class DashboardController {
|
||||
|
||||
@Resource
|
||||
DashboardService dashboardService;
|
||||
|
||||
@RequestMapping(value = "/broker.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object broker(@RequestParam String date) {
|
||||
return dashboardService.queryBrokerData(date);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/topic.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object topic(@RequestParam String date, String topicName) {
|
||||
if (Strings.isNullOrEmpty(topicName)) {
|
||||
return dashboardService.queryTopicData(date);
|
||||
}
|
||||
return dashboardService.queryTopicData(date,topicName);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/topicCurrent", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object topicCurrent() {
|
||||
return dashboardService.queryTopicCurrentData();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||
import org.apache.rocketmq.console.model.MessageView;
|
||||
import org.apache.rocketmq.console.service.MessageService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/message")
|
||||
public class MessageController {
|
||||
private Logger logger = LoggerFactory.getLogger(MessageController.class);
|
||||
@Resource
|
||||
private MessageService messageService;
|
||||
|
||||
@RequestMapping(value = "/viewMessage.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object viewMessage(@RequestParam(required = false) String topic, @RequestParam String msgId) {
|
||||
Map<String, Object> messageViewMap = Maps.newHashMap();
|
||||
Pair<MessageView, List<MessageTrack>> messageViewListPair = messageService.viewMessage(topic, msgId);
|
||||
messageViewMap.put("messageView", messageViewListPair.getObject1());
|
||||
messageViewMap.put("messageTrackList", messageViewListPair.getObject2());
|
||||
return messageViewMap;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/queryMessageByTopicAndKey.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object queryMessageByTopicAndKey(@RequestParam String topic, @RequestParam String key) {
|
||||
return messageService.queryMessageByTopicAndKey(topic, key);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/queryMessageByTopic.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object queryMessageByTopic(@RequestParam String topic, @RequestParam long begin,
|
||||
@RequestParam long end) {
|
||||
return messageService.queryMessageByTopic(topic, begin, end);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/consumeMessageDirectly.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object consumeMessageDirectly(@RequestParam String topic, @RequestParam String consumerGroup,
|
||||
@RequestParam String msgId,
|
||||
@RequestParam(required = false) String clientId) {
|
||||
logger.info("msgId={} consumerGroup={} clientId={}", msgId, consumerGroup, clientId);
|
||||
ConsumeMessageDirectlyResult consumeMessageDirectlyResult = messageService.consumeMessageDirectly(topic, msgId, consumerGroup, clientId);
|
||||
logger.info("consumeMessageDirectlyResult={}", JsonUtil.obj2String(consumeMessageDirectlyResult));
|
||||
return consumeMessageDirectlyResult;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
|
||||
import org.apache.rocketmq.console.service.MonitorService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/monitor")
|
||||
public class MonitorController {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(MonitorController.class);
|
||||
@Resource
|
||||
private MonitorService monitorService;
|
||||
|
||||
@RequestMapping(value = "/createOrUpdateConsumerMonitor.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object createOrUpdateConsumerMonitor(@RequestParam String consumeGroupName, @RequestParam int minCount,
|
||||
@RequestParam int maxDiffTotal) {
|
||||
return monitorService.createOrUpdateConsumerMonitor(consumeGroupName, new ConsumerMonitorConfig(minCount, maxDiffTotal));
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/consumerMonitorConfig.query", method = {RequestMethod.GET})
|
||||
@ResponseBody
|
||||
public Object consumerMonitorConfig() {
|
||||
return monitorService.queryConsumerMonitorConfig();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/consumerMonitorConfigByGroupName.query", method = {RequestMethod.GET})
|
||||
@ResponseBody
|
||||
public Object consumerMonitorConfigByGroupName(@RequestParam String consumeGroupName) {
|
||||
return monitorService.queryConsumerMonitorConfigByGroupName(consumeGroupName);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/deleteConsumerMonitor.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object deleteConsumerMonitor(@RequestParam String consumeGroupName) {
|
||||
return monitorService.deleteConsumerMonitor(consumeGroupName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.aspect.admin.annotation.OriginalControllerReturnValue;
|
||||
import org.apache.rocketmq.console.service.OpsService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/rocketmq")
|
||||
public class NamesvrController {
|
||||
@Resource
|
||||
private OpsService opsService;
|
||||
|
||||
@RequestMapping(value = "/nsaddr", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
@OriginalControllerReturnValue
|
||||
public Object nsaddr() {
|
||||
return opsService.getNameSvrList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.service.OpsService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/ops")
|
||||
public class OpsController {
|
||||
|
||||
@Resource
|
||||
private OpsService opsService;
|
||||
|
||||
@RequestMapping(value = "/homePage.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object homePage() {
|
||||
return opsService.homePageInfo();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/updateNameSvrAddr.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object updateNameSvrAddr(@RequestParam String nameSvrAddrList) {
|
||||
opsService.updateNameSvrAddrList(nameSvrAddrList);
|
||||
return true;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/updateIsVIPChannel.do", method = RequestMethod.POST)
|
||||
@ResponseBody
|
||||
public Object updateIsVIPChannel(@RequestParam String useVIPChannel) {
|
||||
opsService.updateIsVIPChannel(useVIPChannel);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/rocketMqStatus.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object clusterStatus() {
|
||||
return opsService.rocketMqStatusCheck();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
|
||||
import org.apache.rocketmq.console.model.ConnectionInfo;
|
||||
import org.apache.rocketmq.console.service.ProducerService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/producer")
|
||||
public class ProducerController {
|
||||
|
||||
@Resource
|
||||
private ProducerService producerService;
|
||||
|
||||
@RequestMapping(value = "/producerConnection.query", method = {RequestMethod.GET})
|
||||
@ResponseBody
|
||||
public Object producerConnection(@RequestParam String producerGroup, @RequestParam String topic) {
|
||||
ProducerConnection producerConnection = producerService.getProducerConnection(producerGroup, topic);
|
||||
producerConnection.setConnectionSet(ConnectionInfo.buildConnectionInfoHashSet(producerConnection.getConnectionSet()));
|
||||
return producerConnection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
|
||||
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
|
||||
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
|
||||
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.client.producer.DefaultMQProducer;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/test")
|
||||
public class TestController {
|
||||
private Logger logger = LoggerFactory.getLogger(TestController.class);
|
||||
private String testTopic = "TestTopic";
|
||||
|
||||
@Resource
|
||||
private RMQConfigure rMQConfigure;
|
||||
|
||||
@RequestMapping(value = "/runTask.do", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object list() throws MQClientException, RemotingException, InterruptedException {
|
||||
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(testTopic + "Group");
|
||||
consumer.setNamesrvAddr(rMQConfigure.getNamesrvAddr());
|
||||
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
|
||||
consumer.subscribe(testTopic, "*");
|
||||
consumer.registerMessageListener(new MessageListenerConcurrently() {
|
||||
|
||||
@Override
|
||||
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
|
||||
ConsumeConcurrentlyContext context) {
|
||||
logger.info("receiveMessage msgSize={}", msgs.size());
|
||||
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
|
||||
}
|
||||
});
|
||||
consumer.start();
|
||||
final DefaultMQProducer producer = new DefaultMQProducer(testTopic + "Group");
|
||||
producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
|
||||
producer.setNamesrvAddr(rMQConfigure.getNamesrvAddr());
|
||||
producer.start();
|
||||
|
||||
new Thread(new Runnable() {
|
||||
|
||||
@Override public void run() {
|
||||
|
||||
int i = 0;
|
||||
while (true) {
|
||||
try {
|
||||
Message msg = new Message(testTopic,
|
||||
"TagA" + i,
|
||||
"KEYS" + i,
|
||||
("Hello RocketMQ " + i).getBytes()
|
||||
);
|
||||
Thread.sleep(1000L);
|
||||
SendResult sendResult = producer.send(msg);
|
||||
logger.info("sendMessage={}", JsonUtil.obj2String(sendResult));
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.controller;
|
||||
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
|
||||
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
|
||||
import org.apache.rocketmq.console.service.ConsumerService;
|
||||
import org.apache.rocketmq.console.service.TopicService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/topic")
|
||||
public class TopicController {
|
||||
private Logger logger = LoggerFactory.getLogger(TopicController.class);
|
||||
|
||||
@Resource
|
||||
private TopicService topicService;
|
||||
|
||||
@Resource
|
||||
private ConsumerService consumerService;
|
||||
|
||||
@RequestMapping(value = "/list.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object list() throws MQClientException, RemotingException, InterruptedException {
|
||||
return topicService.fetchAllTopicList();
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/stats.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object stats(@RequestParam String topic) {
|
||||
return topicService.stats(topic);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/route.query", method = RequestMethod.GET)
|
||||
@ResponseBody
|
||||
public Object route(@RequestParam String topic) {
|
||||
return topicService.route(topic);
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/createOrUpdate.do", method = { RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object topicCreateOrUpdateRequest(@RequestBody TopicConfigInfo topicCreateOrUpdateRequest) {
|
||||
Preconditions.checkArgument(CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getBrokerNameList()) || CollectionUtils.isNotEmpty(topicCreateOrUpdateRequest.getClusterNameList()),
|
||||
"clusterName or brokerName can not be all blank");
|
||||
logger.info("op=look topicCreateOrUpdateRequest={}", JsonUtil.obj2String(topicCreateOrUpdateRequest));
|
||||
topicService.createOrUpdate(topicCreateOrUpdateRequest);
|
||||
return true;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/queryConsumerByTopic.query")
|
||||
@ResponseBody
|
||||
public Object queryConsumerByTopic(@RequestParam String topic) {
|
||||
return consumerService.queryConsumeStatsListByTopicName(topic);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/queryTopicConsumerInfo.query")
|
||||
@ResponseBody
|
||||
public Object queryTopicConsumerInfo(@RequestParam String topic) {
|
||||
return topicService.queryTopicConsumerInfo(topic);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/examineTopicConfig.query")
|
||||
@ResponseBody
|
||||
public Object examineTopicConfig(@RequestParam String topic,
|
||||
@RequestParam(required = false) String brokerName) throws RemotingException, MQClientException, InterruptedException {
|
||||
return topicService.examineTopicConfig(topic);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/sendTopicMessage.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object sendTopicMessage(
|
||||
@RequestBody SendTopicMessageRequest sendTopicMessageRequest) throws RemotingException, MQClientException, InterruptedException {
|
||||
return topicService.sendTopicMessageRequest(sendTopicMessageRequest);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/deleteTopic.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object delete(@RequestParam(required = false) String clusterName, @RequestParam String topic) {
|
||||
return topicService.deleteTopic(topic, clusterName);
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/deleteTopicByBroker.do", method = {RequestMethod.POST})
|
||||
@ResponseBody
|
||||
public Object deleteTopicByBroker(@RequestParam String brokerName, @RequestParam String topic) {
|
||||
return topicService.deleteTopicInBroker(brokerName, topic);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.exception;
|
||||
|
||||
public class ServiceException extends RuntimeException {
|
||||
private static final long serialVersionUID = 9213584003139969215L;
|
||||
private int code;
|
||||
|
||||
public ServiceException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import org.apache.rocketmq.common.MQVersion;
|
||||
import org.apache.rocketmq.common.protocol.body.Connection;
|
||||
|
||||
public class ConnectionInfo extends Connection {
|
||||
private String versionDesc;
|
||||
|
||||
public static ConnectionInfo buildConnectionInfo(Connection connection) {
|
||||
ConnectionInfo connectionInfo = new ConnectionInfo();
|
||||
connectionInfo.setClientId(connection.getClientId());
|
||||
connectionInfo.setClientAddr(connection.getClientAddr());
|
||||
connectionInfo.setLanguage(connection.getLanguage());
|
||||
connectionInfo.setVersion(connection.getVersion());
|
||||
connectionInfo.setVersionDesc(MQVersion.getVersionDesc(connection.getVersion()));
|
||||
return connectionInfo;
|
||||
}
|
||||
|
||||
public static HashSet<Connection> buildConnectionInfoHashSet(Collection<Connection> connectionList) {
|
||||
HashSet<Connection> connectionHashSet = Sets.newHashSet();
|
||||
for (Connection connection : connectionList) {
|
||||
connectionHashSet.add(buildConnectionInfo(connection));
|
||||
}
|
||||
return connectionHashSet;
|
||||
}
|
||||
|
||||
public String getVersionDesc() {
|
||||
return versionDesc;
|
||||
}
|
||||
|
||||
public void setVersionDesc(String versionDesc) {
|
||||
this.versionDesc = versionDesc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
import org.apache.rocketmq.common.admin.RollbackStats;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ConsumerGroupRollBackStat {
|
||||
private boolean status;
|
||||
private String errMsg;
|
||||
private List<RollbackStats> rollbackStatsList = Lists.newArrayList();
|
||||
|
||||
public ConsumerGroupRollBackStat(boolean status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public ConsumerGroupRollBackStat(boolean status, String errMsg) {
|
||||
this.status = status;
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
|
||||
public String getErrMsg() {
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
public void setErrMsg(String errMsg) {
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
|
||||
public boolean isStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(boolean status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public List<RollbackStats> getRollbackStatsList() {
|
||||
return rollbackStatsList;
|
||||
}
|
||||
|
||||
public void setRollbackStatsList(List<RollbackStats> rollbackStatsList) {
|
||||
this.rollbackStatsList = rollbackStatsList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
public class ConsumerMonitorConfig {
|
||||
private int minCount;
|
||||
private int maxDiffTotal;
|
||||
|
||||
public ConsumerMonitorConfig() {
|
||||
}
|
||||
|
||||
public ConsumerMonitorConfig(int minCount, int maxDiffTotal) {
|
||||
this.minCount = minCount;
|
||||
this.maxDiffTotal = maxDiffTotal;
|
||||
}
|
||||
|
||||
public int getMinCount() {
|
||||
return minCount;
|
||||
}
|
||||
|
||||
public void setMinCount(int minCount) {
|
||||
this.minCount = minCount;
|
||||
}
|
||||
|
||||
public int getMaxDiffTotal() {
|
||||
return maxDiffTotal;
|
||||
}
|
||||
|
||||
public void setMaxDiffTotal(int maxDiffTotal) {
|
||||
this.maxDiffTotal = maxDiffTotal;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.ConsumeType;
|
||||
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
|
||||
|
||||
public class GroupConsumeInfo implements Comparable<GroupConsumeInfo> {
|
||||
private String group;
|
||||
private String version;
|
||||
private int count;
|
||||
private ConsumeType consumeType;
|
||||
private MessageModel messageModel;
|
||||
private int consumeTps;
|
||||
private long diffTotal = -1;
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public void setGroup(String group) {
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public ConsumeType getConsumeType() {
|
||||
return consumeType;
|
||||
}
|
||||
|
||||
public void setConsumeType(ConsumeType consumeType) {
|
||||
this.consumeType = consumeType;
|
||||
}
|
||||
|
||||
public MessageModel getMessageModel() {
|
||||
return messageModel;
|
||||
}
|
||||
|
||||
public void setMessageModel(MessageModel messageModel) {
|
||||
this.messageModel = messageModel;
|
||||
}
|
||||
|
||||
public long getDiffTotal() {
|
||||
return diffTotal;
|
||||
}
|
||||
|
||||
public void setDiffTotal(long diffTotal) {
|
||||
this.diffTotal = diffTotal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(GroupConsumeInfo o) {
|
||||
if (this.count != o.count) {
|
||||
return o.count - this.count;
|
||||
}
|
||||
|
||||
return (int) (o.diffTotal - diffTotal);
|
||||
}
|
||||
|
||||
public int getConsumeTps() {
|
||||
return consumeTps;
|
||||
}
|
||||
|
||||
public void setConsumeTps(int consumeTps) {
|
||||
this.consumeTps = consumeTps;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
196
src/main/java/org/apache/rocketmq/console/model/MessageView.java
Normal file
196
src/main/java/org/apache/rocketmq/console/model/MessageView.java
Normal file
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
|
||||
public class MessageView {
|
||||
|
||||
/** from MessageExt **/
|
||||
private int queueId;
|
||||
private int storeSize;
|
||||
private long queueOffset;
|
||||
private int sysFlag;
|
||||
private long bornTimestamp;
|
||||
private SocketAddress bornHost;
|
||||
private long storeTimestamp;
|
||||
private SocketAddress storeHost;
|
||||
private String msgId;
|
||||
private long commitLogOffset;
|
||||
private int bodyCRC;
|
||||
private int reconsumeTimes;
|
||||
private long preparedTransactionOffset;
|
||||
/**from MessageExt**/
|
||||
|
||||
/** from Message **/
|
||||
private String topic;
|
||||
private int flag;
|
||||
private Map<String, String> properties;
|
||||
private String messageBody; // body
|
||||
|
||||
/** from Message **/
|
||||
|
||||
public static MessageView fromMessageExt(MessageExt messageExt) {
|
||||
MessageView messageView = new MessageView();
|
||||
BeanUtils.copyProperties(messageExt, messageView);
|
||||
if (messageExt.getBody() != null) {
|
||||
messageView.setMessageBody(new String(messageExt.getBody(), Charsets.UTF_8));
|
||||
}
|
||||
return messageView;
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public int getFlag() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
public void setFlag(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public Map<String, String> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Map<String, String> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public int getQueueId() {
|
||||
return queueId;
|
||||
}
|
||||
|
||||
public void setQueueId(int queueId) {
|
||||
this.queueId = queueId;
|
||||
}
|
||||
|
||||
public int getStoreSize() {
|
||||
return storeSize;
|
||||
}
|
||||
|
||||
public void setStoreSize(int storeSize) {
|
||||
this.storeSize = storeSize;
|
||||
}
|
||||
|
||||
public long getQueueOffset() {
|
||||
return queueOffset;
|
||||
}
|
||||
|
||||
public void setQueueOffset(long queueOffset) {
|
||||
this.queueOffset = queueOffset;
|
||||
}
|
||||
|
||||
public int getSysFlag() {
|
||||
return sysFlag;
|
||||
}
|
||||
|
||||
public void setSysFlag(int sysFlag) {
|
||||
this.sysFlag = sysFlag;
|
||||
}
|
||||
|
||||
public long getBornTimestamp() {
|
||||
return bornTimestamp;
|
||||
}
|
||||
|
||||
public void setBornTimestamp(long bornTimestamp) {
|
||||
this.bornTimestamp = bornTimestamp;
|
||||
}
|
||||
|
||||
public SocketAddress getBornHost() {
|
||||
return bornHost;
|
||||
}
|
||||
|
||||
public void setBornHost(SocketAddress bornHost) {
|
||||
this.bornHost = bornHost;
|
||||
}
|
||||
|
||||
public long getStoreTimestamp() {
|
||||
return storeTimestamp;
|
||||
}
|
||||
|
||||
public void setStoreTimestamp(long storeTimestamp) {
|
||||
this.storeTimestamp = storeTimestamp;
|
||||
}
|
||||
|
||||
public SocketAddress getStoreHost() {
|
||||
return storeHost;
|
||||
}
|
||||
|
||||
public void setStoreHost(SocketAddress storeHost) {
|
||||
this.storeHost = storeHost;
|
||||
}
|
||||
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
public long getCommitLogOffset() {
|
||||
return commitLogOffset;
|
||||
}
|
||||
|
||||
public void setCommitLogOffset(long commitLogOffset) {
|
||||
this.commitLogOffset = commitLogOffset;
|
||||
}
|
||||
|
||||
public int getBodyCRC() {
|
||||
return bodyCRC;
|
||||
}
|
||||
|
||||
public void setBodyCRC(int bodyCRC) {
|
||||
this.bodyCRC = bodyCRC;
|
||||
}
|
||||
|
||||
public int getReconsumeTimes() {
|
||||
return reconsumeTimes;
|
||||
}
|
||||
|
||||
public void setReconsumeTimes(int reconsumeTimes) {
|
||||
this.reconsumeTimes = reconsumeTimes;
|
||||
}
|
||||
|
||||
public long getPreparedTransactionOffset() {
|
||||
return preparedTransactionOffset;
|
||||
}
|
||||
|
||||
public void setPreparedTransactionOffset(long preparedTransactionOffset) {
|
||||
this.preparedTransactionOffset = preparedTransactionOffset;
|
||||
}
|
||||
|
||||
public String getMessageBody() {
|
||||
return messageBody;
|
||||
}
|
||||
|
||||
public void setMessageBody(String messageBody) {
|
||||
this.messageBody = messageBody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
import org.apache.rocketmq.common.admin.OffsetWrapper;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
public class QueueStatInfo {
|
||||
private String brokerName;
|
||||
private int queueId;
|
||||
private long brokerOffset;
|
||||
private long consumerOffset;
|
||||
private String clientInfo;
|
||||
private long lastTimestamp;
|
||||
|
||||
public static QueueStatInfo fromOffsetTableEntry(MessageQueue key, OffsetWrapper value) {
|
||||
QueueStatInfo queueStatInfo = new QueueStatInfo();
|
||||
BeanUtils.copyProperties(key, queueStatInfo);
|
||||
BeanUtils.copyProperties(value, queueStatInfo);
|
||||
return queueStatInfo;
|
||||
}
|
||||
|
||||
public String getClientInfo() {
|
||||
return clientInfo;
|
||||
}
|
||||
|
||||
public void setClientInfo(String clientInfo) {
|
||||
this.clientInfo = clientInfo;
|
||||
}
|
||||
|
||||
public String getBrokerName() {
|
||||
return brokerName;
|
||||
}
|
||||
|
||||
public void setBrokerName(String brokerName) {
|
||||
this.brokerName = brokerName;
|
||||
}
|
||||
|
||||
public int getQueueId() {
|
||||
return queueId;
|
||||
}
|
||||
|
||||
public void setQueueId(int queueId) {
|
||||
this.queueId = queueId;
|
||||
}
|
||||
|
||||
public long getBrokerOffset() {
|
||||
return brokerOffset;
|
||||
}
|
||||
|
||||
public void setBrokerOffset(long brokerOffset) {
|
||||
this.brokerOffset = brokerOffset;
|
||||
}
|
||||
|
||||
public long getConsumerOffset() {
|
||||
return consumerOffset;
|
||||
}
|
||||
|
||||
public void setConsumerOffset(long consumerOffset) {
|
||||
this.consumerOffset = consumerOffset;
|
||||
}
|
||||
|
||||
public long getLastTimestamp() {
|
||||
return lastTimestamp;
|
||||
}
|
||||
|
||||
public void setLastTimestamp(long lastTimestamp) {
|
||||
this.lastTimestamp = lastTimestamp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TopicConsumerInfo {
|
||||
private String topic;
|
||||
private long diffTotal;
|
||||
private long lastTimestamp;
|
||||
private List<QueueStatInfo> queueStatInfoList = Lists.newArrayList();
|
||||
|
||||
public TopicConsumerInfo(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public long getDiffTotal() {
|
||||
return diffTotal;
|
||||
}
|
||||
|
||||
public void setDiffTotal(long diffTotal) {
|
||||
this.diffTotal = diffTotal;
|
||||
}
|
||||
|
||||
public List<QueueStatInfo> getQueueStatInfoList() {
|
||||
return queueStatInfoList;
|
||||
}
|
||||
|
||||
public long getLastTimestamp() {
|
||||
return lastTimestamp;
|
||||
}
|
||||
|
||||
public void appendQueueStatInfo(QueueStatInfo queueStatInfo) {
|
||||
queueStatInfoList.add(queueStatInfo);
|
||||
diffTotal = diffTotal + (queueStatInfo.getBrokerOffset() - queueStatInfo.getConsumerOffset());
|
||||
lastTimestamp = Math.max(lastTimestamp, queueStatInfo.getLastTimestamp());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model.request;
|
||||
|
||||
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ConsumerConfigInfo {
|
||||
private List<String> clusterNameList;
|
||||
|
||||
private List<String> brokerNameList;
|
||||
private SubscriptionGroupConfig subscriptionGroupConfig;
|
||||
|
||||
public ConsumerConfigInfo() {
|
||||
}
|
||||
|
||||
public ConsumerConfigInfo(List<String> brokerNameList, SubscriptionGroupConfig subscriptionGroupConfig) {
|
||||
this.brokerNameList = brokerNameList;
|
||||
this.subscriptionGroupConfig = subscriptionGroupConfig;
|
||||
}
|
||||
|
||||
public List<String> getClusterNameList() {
|
||||
return clusterNameList;
|
||||
}
|
||||
|
||||
public void setClusterNameList(List<String> clusterNameList) {
|
||||
this.clusterNameList = clusterNameList;
|
||||
}
|
||||
|
||||
public List<String> getBrokerNameList() {
|
||||
return brokerNameList;
|
||||
}
|
||||
|
||||
public void setBrokerNameList(List<String> brokerNameList) {
|
||||
this.brokerNameList = brokerNameList;
|
||||
}
|
||||
|
||||
public SubscriptionGroupConfig getSubscriptionGroupConfig() {
|
||||
return subscriptionGroupConfig;
|
||||
}
|
||||
|
||||
public void setSubscriptionGroupConfig(SubscriptionGroupConfig subscriptionGroupConfig) {
|
||||
this.subscriptionGroupConfig = subscriptionGroupConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model.request;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DeleteSubGroupRequest {
|
||||
private String groupName;
|
||||
private List<String> brokerNameList;
|
||||
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
|
||||
public List<String> getBrokerNameList() {
|
||||
return brokerNameList;
|
||||
}
|
||||
|
||||
public void setBrokerNameList(List<String> brokerNameList) {
|
||||
this.brokerNameList = brokerNameList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model.request;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ResetOffsetRequest {
|
||||
private List<String> consumerGroupList;
|
||||
private String topic;
|
||||
private long resetTime;
|
||||
private boolean force;
|
||||
|
||||
public List<String> getConsumerGroupList() {
|
||||
return consumerGroupList;
|
||||
}
|
||||
|
||||
public void setConsumerGroupList(List<String> consumerGroupList) {
|
||||
this.consumerGroupList = consumerGroupList;
|
||||
}
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public long getResetTime() {
|
||||
return resetTime;
|
||||
}
|
||||
|
||||
public void setResetTime(long resetTime) {
|
||||
this.resetTime = resetTime;
|
||||
}
|
||||
|
||||
public boolean isForce() {
|
||||
return force;
|
||||
}
|
||||
|
||||
public void setForce(boolean force) {
|
||||
this.force = force;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model.request;
|
||||
|
||||
public class SendTopicMessageRequest {
|
||||
private String topic;
|
||||
private String key;
|
||||
private String tag;
|
||||
private String messageBody;
|
||||
|
||||
public String getTopic() {
|
||||
return topic;
|
||||
}
|
||||
|
||||
public void setTopic(String topic) {
|
||||
this.topic = topic;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
public void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String getMessageBody() {
|
||||
return messageBody;
|
||||
}
|
||||
|
||||
public void setMessageBody(String messageBody) {
|
||||
this.messageBody = messageBody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.model.request;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TopicConfigInfo {
|
||||
|
||||
private List<String> clusterNameList;
|
||||
private List<String> brokerNameList;
|
||||
|
||||
/** topicConfig */
|
||||
private String topicName;
|
||||
private int writeQueueNums;
|
||||
private int readQueueNums;
|
||||
private int perm;
|
||||
private boolean order;
|
||||
|
||||
public List<String> getClusterNameList() {
|
||||
return clusterNameList;
|
||||
}
|
||||
|
||||
public void setClusterNameList(List<String> clusterNameList) {
|
||||
this.clusterNameList = clusterNameList;
|
||||
}
|
||||
|
||||
/** topicConfig */
|
||||
|
||||
|
||||
|
||||
public List<String> getBrokerNameList() {
|
||||
return brokerNameList;
|
||||
}
|
||||
|
||||
public void setBrokerNameList(List<String> brokerNameList) {
|
||||
this.brokerNameList = brokerNameList;
|
||||
}
|
||||
|
||||
public String getTopicName() {
|
||||
return topicName;
|
||||
}
|
||||
|
||||
public void setTopicName(String topicName) {
|
||||
this.topicName = topicName;
|
||||
}
|
||||
|
||||
public int getWriteQueueNums() {
|
||||
return writeQueueNums;
|
||||
}
|
||||
|
||||
public void setWriteQueueNums(int writeQueueNums) {
|
||||
this.writeQueueNums = writeQueueNums;
|
||||
}
|
||||
|
||||
public int getReadQueueNums() {
|
||||
return readQueueNums;
|
||||
}
|
||||
|
||||
public void setReadQueueNums(int readQueueNums) {
|
||||
this.readQueueNums = readQueueNums;
|
||||
}
|
||||
|
||||
public int getPerm() {
|
||||
return perm;
|
||||
}
|
||||
|
||||
public void setPerm(int perm) {
|
||||
this.perm = perm;
|
||||
}
|
||||
|
||||
public boolean isOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(boolean order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
TopicConfigInfo that = (TopicConfigInfo) o;
|
||||
return writeQueueNums == that.writeQueueNums &&
|
||||
readQueueNums == that.readQueueNums &&
|
||||
perm == that.perm &&
|
||||
order == that.order &&
|
||||
Objects.equal(topicName, that.topicName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(topicName, writeQueueNums, readQueueNums, perm, order);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
public abstract class AbstractCommonService {
|
||||
@Resource
|
||||
protected MQAdminExt mqAdminExt;
|
||||
protected final Set<String> changeToBrokerNameSet(HashMap<String, Set<String>> clusterAddrTable,
|
||||
List<String> clusterNameList, List<String> brokerNameList) {
|
||||
Set<String> finalBrokerNameList = Sets.newHashSet();
|
||||
if (CollectionUtils.isNotEmpty(clusterNameList)) {
|
||||
try {
|
||||
for (String clusterName : clusterNameList) {
|
||||
finalBrokerNameList.addAll(clusterAddrTable.get(clusterName));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(brokerNameList)) {
|
||||
finalBrokerNameList.addAll(brokerNameList);
|
||||
}
|
||||
return finalBrokerNameList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public interface ClusterService {
|
||||
Map<String, Object> list();
|
||||
|
||||
Properties getBrokerConfig(String brokerAddr);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.console.model.ConsumerGroupRollBackStat;
|
||||
import org.apache.rocketmq.console.model.GroupConsumeInfo;
|
||||
import org.apache.rocketmq.console.model.TopicConsumerInfo;
|
||||
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
|
||||
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
|
||||
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ConsumerService {
|
||||
List<GroupConsumeInfo> queryGroupList();
|
||||
|
||||
GroupConsumeInfo queryGroup(String consumerGroup);
|
||||
|
||||
|
||||
List<TopicConsumerInfo> queryConsumeStatsListByGroupName(String groupName);
|
||||
|
||||
List<TopicConsumerInfo> queryConsumeStatsList(String topic, String groupName);
|
||||
|
||||
Map<String, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic);
|
||||
|
||||
Map<String /*consumerGroup*/, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest);
|
||||
|
||||
List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group);
|
||||
|
||||
boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest);
|
||||
|
||||
boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo);
|
||||
|
||||
Set<String> fetchBrokerNameSetBySubscriptionGroup(String group);
|
||||
|
||||
ConsumerConnection getConsumerConnection(String consumerGroup);
|
||||
|
||||
ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface DashboardCollectService {
|
||||
// todo just move the task to org.apache.rocketmq.console.task.DashboardCollectTask
|
||||
// the code can be reconstruct
|
||||
LoadingCache<String, List<String>> getBrokerMap();
|
||||
|
||||
LoadingCache<String, List<String>> getTopicMap();
|
||||
|
||||
Map<String, List<String>> jsonDataFile2map(File file);
|
||||
|
||||
Map<String, List<String>> getBrokerCache(String date);
|
||||
|
||||
Map<String, List<String>> getTopicCache(String date);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface DashboardService {
|
||||
/**
|
||||
* @param date format yyyy-MM-dd
|
||||
*/
|
||||
Map<String, List<String>> queryBrokerData(String date);
|
||||
|
||||
/**
|
||||
* @param date format yyyy-MM-dd
|
||||
*/
|
||||
Map<String, List<String>> queryTopicData(String date);
|
||||
|
||||
/**
|
||||
* @param date format yyyy-MM-dd
|
||||
* @param topicName
|
||||
*/
|
||||
List<String> queryTopicData(String date, String topicName);
|
||||
|
||||
List<String> queryTopicCurrentData();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||
import org.apache.rocketmq.console.model.MessageView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MessageService {
|
||||
/**
|
||||
* @param subject
|
||||
* @param msgId
|
||||
*/
|
||||
Pair<MessageView, List<MessageTrack>> viewMessage(String subject, final String msgId);
|
||||
|
||||
List<MessageView> queryMessageByTopicAndKey(final String topic, final String key);
|
||||
|
||||
/**
|
||||
* @param topic
|
||||
* @param begin
|
||||
* @param end
|
||||
* org.apache.rocketmq.tools.command.message.PrintMessageSubCommand
|
||||
*/
|
||||
List<MessageView> queryMessageByTopic(final String topic, final long begin,
|
||||
final long end);
|
||||
|
||||
List<MessageTrack> messageTrackDetail(MessageExt msg);
|
||||
|
||||
ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup,
|
||||
String clientId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import java.util.Map;
|
||||
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
|
||||
|
||||
public interface MonitorService {
|
||||
boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config);
|
||||
|
||||
Map<String, ConsumerMonitorConfig> queryConsumerMonitorConfig();
|
||||
|
||||
ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName);
|
||||
|
||||
boolean deleteConsumerMonitor(String consumeGroupName);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import java.util.Map;
|
||||
import org.apache.rocketmq.console.service.checker.CheckerType;
|
||||
|
||||
public interface OpsService {
|
||||
Map<String, Object> homePageInfo();
|
||||
|
||||
void updateNameSvrAddrList(String nameSvrAddrList);
|
||||
|
||||
String getNameSvrList();
|
||||
|
||||
Map<CheckerType,Object> rocketMqStatusCheck();
|
||||
|
||||
boolean updateIsVIPChannel(String useVIPChannel);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
|
||||
|
||||
public interface ProducerService {
|
||||
ProducerConnection getProducerConnection(String producerGroup, String topic);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service;
|
||||
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.TopicConfig;
|
||||
import org.apache.rocketmq.common.admin.TopicStatsTable;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
|
||||
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TopicService {
|
||||
TopicList fetchAllTopicList();
|
||||
|
||||
TopicStatsTable stats(String topic);
|
||||
|
||||
TopicRouteData route(String topic);
|
||||
|
||||
GroupList queryTopicConsumerInfo(String topic);
|
||||
|
||||
void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest);
|
||||
|
||||
TopicConfig examineTopicConfig(String topic, String brokerName);
|
||||
|
||||
List<TopicConfigInfo> examineTopicConfig(String topic);
|
||||
|
||||
boolean deleteTopic(String topic, String clusterName);
|
||||
|
||||
boolean deleteTopic(String topic);
|
||||
|
||||
boolean deleteTopicInBroker(String brokerName, String topic);
|
||||
|
||||
SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.checker;
|
||||
|
||||
public enum CheckerType {
|
||||
CLUSTER_HEALTH_CHECK,
|
||||
TOPIC_ONLY_ONE_BROKER_CHECK
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.checker;
|
||||
|
||||
public interface RocketMqChecker {
|
||||
public Object doCheck();
|
||||
|
||||
public CheckerType checkerType();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.checker.impl;
|
||||
|
||||
import org.apache.rocketmq.console.service.checker.CheckerType;
|
||||
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ClusterHealthCheckerImpl implements RocketMqChecker {
|
||||
@Override
|
||||
public Object doCheck() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckerType checkerType() {
|
||||
return CheckerType.CLUSTER_HEALTH_CHECK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.checker.impl;
|
||||
|
||||
import org.apache.rocketmq.console.service.checker.CheckerType;
|
||||
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TopicOnlyOneBrokerCheckerImpl implements RocketMqChecker {
|
||||
@Override
|
||||
public Object doCheck() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckerType checkerType() {
|
||||
return CheckerType.TOPIC_ONLY_ONE_BROKER_CHECK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,501 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.client;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import org.apache.rocketmq.client.QueryResult;
|
||||
import org.apache.rocketmq.client.exception.MQBrokerException;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.client.impl.MQAdminImpl;
|
||||
import org.apache.rocketmq.common.TopicConfig;
|
||||
import org.apache.rocketmq.common.admin.ConsumeStats;
|
||||
import org.apache.rocketmq.common.admin.RollbackStats;
|
||||
import org.apache.rocketmq.common.admin.TopicStatsTable;
|
||||
import org.apache.rocketmq.common.message.MessageClientIDSetter;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
import org.apache.rocketmq.common.protocol.RequestCode;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeStatsList;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.KVTable;
|
||||
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
|
||||
import org.apache.rocketmq.common.protocol.body.QueueTimeSpan;
|
||||
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicConfigSerializeWrapper;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import org.apache.rocketmq.remoting.RemotingClient;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingCommandException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingConnectException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingSendRequestException;
|
||||
import org.apache.rocketmq.remoting.exception.RemotingTimeoutException;
|
||||
import org.apache.rocketmq.remoting.protocol.RemotingCommand;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||
import org.joor.Reflect;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static org.apache.rocketmq.remoting.protocol.RemotingSerializable.decode;
|
||||
|
||||
@Service
|
||||
public class MQAdminExtImpl implements MQAdminExt {
|
||||
private Logger logger = LoggerFactory.getLogger(MQAdminExtImpl.class);
|
||||
|
||||
public MQAdminExtImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokerConfig(String brokerAddr, Properties properties)
|
||||
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
|
||||
UnsupportedEncodingException, InterruptedException, MQBrokerException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().updateBrokerConfig(brokerAddr, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndUpdateTopicConfig(String addr, TopicConfig config)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateTopicConfig(addr, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndUpdateSubscriptionGroupConfig(String addr, SubscriptionGroupConfig config)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateSubscriptionGroupConfig(addr, config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionGroupConfig examineSubscriptionGroupConfig(String addr, String group) {
|
||||
RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
|
||||
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_SUBSCRIPTIONGROUP_CONFIG, null);
|
||||
RemotingCommand response = null;
|
||||
try {
|
||||
response = remotingClient.invokeSync(addr, request, 3000);
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
assert response != null;
|
||||
switch (response.getCode()) {
|
||||
case ResponseCode.SUCCESS: {
|
||||
SubscriptionGroupWrapper subscriptionGroupWrapper = decode(response.getBody(), SubscriptionGroupWrapper.class);
|
||||
return subscriptionGroupWrapper.getSubscriptionGroupTable().get(group);
|
||||
}
|
||||
default:
|
||||
throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicConfig examineTopicConfig(String addr, String topic) {
|
||||
RemotingClient remotingClient = MQAdminInstance.threadLocalRemotingClient();
|
||||
RemotingCommand request = RemotingCommand.createRequestCommand(RequestCode.GET_ALL_TOPIC_CONFIG, null);
|
||||
RemotingCommand response = null;
|
||||
try {
|
||||
response = remotingClient.invokeSync(addr, request, 3000);
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
switch (response.getCode()) {
|
||||
case ResponseCode.SUCCESS: {
|
||||
TopicConfigSerializeWrapper topicConfigSerializeWrapper = decode(response.getBody(), TopicConfigSerializeWrapper.class);
|
||||
return topicConfigSerializeWrapper.getTopicConfigTable().get(topic);
|
||||
}
|
||||
default:
|
||||
throw Throwables.propagate(new MQBrokerException(response.getCode(), response.getRemark()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicStatsTable examineTopicStats(String topic)
|
||||
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineTopicStats(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicList fetchAllTopicList() throws RemotingException, MQClientException, InterruptedException {
|
||||
TopicList topicList = MQAdminInstance.threadLocalMQAdminExt().fetchAllTopicList();
|
||||
logger.debug("op=look={}", JsonUtil.obj2String(topicList.getTopicList()));
|
||||
return topicList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KVTable fetchBrokerRuntimeStats(String brokerAddr)
|
||||
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
|
||||
InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().fetchBrokerRuntimeStats(brokerAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeStats examineConsumeStats(String consumerGroup)
|
||||
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineConsumeStats(consumerGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeStats examineConsumeStats(String consumerGroup, String topic)
|
||||
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineConsumeStats(consumerGroup, topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterInfo examineBrokerClusterInfo()
|
||||
throws InterruptedException, MQBrokerException, RemotingTimeoutException, RemotingSendRequestException,
|
||||
RemotingConnectException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineBrokerClusterInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicRouteData examineTopicRouteInfo(String topic)
|
||||
throws RemotingException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineTopicRouteInfo(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerConnection examineConsumerConnectionInfo(String consumerGroup)
|
||||
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
|
||||
InterruptedException, MQBrokerException, RemotingException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineConsumerConnectionInfo(consumerGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProducerConnection examineProducerConnectionInfo(String producerGroup, String topic)
|
||||
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().examineProducerConnectionInfo(producerGroup, topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNameServerAddressList() {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getNameServerAddressList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int wipeWritePermOfBroker(String namesrvAddr, String brokerName)
|
||||
throws RemotingCommandException, RemotingConnectException, RemotingSendRequestException,
|
||||
RemotingTimeoutException, InterruptedException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().wipeWritePermOfBroker(namesrvAddr, brokerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putKVConfig(String namespace, String key, String value) {
|
||||
MQAdminInstance.threadLocalMQAdminExt().putKVConfig(namespace, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKVConfig(String namespace, String key)
|
||||
throws RemotingException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getKVConfig(namespace, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KVTable getKVListByNamespace(String namespace)
|
||||
throws RemotingException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getKVListByNamespace(namespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTopicInBroker(Set<String> addrs, String topic)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
logger.info("addrs={} topic={}", JsonUtil.obj2String(addrs), topic);
|
||||
MQAdminInstance.threadLocalMQAdminExt().deleteTopicInBroker(addrs, topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTopicInNameServer(Set<String> addrs, String topic)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().deleteTopicInNameServer(addrs, topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSubscriptionGroup(String addr, String groupName)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().deleteSubscriptionGroup(addr, groupName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createAndUpdateKvConfig(String namespace, String key, String value)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createAndUpdateKvConfig(namespace, key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteKvConfig(String namespace, String key)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().deleteKvConfig(namespace, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RollbackStats> resetOffsetByTimestampOld(String consumerGroup, String topic, long timestamp,
|
||||
boolean force) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().resetOffsetByTimestampOld(consumerGroup, topic, timestamp, force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MessageQueue, Long> resetOffsetByTimestamp(String topic, String group, long timestamp,
|
||||
boolean isForce) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().resetOffsetByTimestamp(topic, group, timestamp, isForce);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetOffsetNew(String consumerGroup, String topic, long timestamp)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().resetOffsetNew(consumerGroup, topic, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Map<MessageQueue, Long>> getConsumeStatus(String topic, String group,
|
||||
String clientAddr) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getConsumeStatus(topic, group, clientAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOrUpdateOrderConf(String key, String value, boolean isCluster)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createOrUpdateOrderConf(key, value, isCluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupList queryTopicConsumeByWho(String topic)
|
||||
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException,
|
||||
InterruptedException, MQBrokerException, RemotingException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().queryTopicConsumeByWho(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanExpiredConsumerQueue(String cluster)
|
||||
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException,
|
||||
InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().cleanExpiredConsumerQueue(cluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanExpiredConsumerQueueByAddr(String addr)
|
||||
throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException,
|
||||
InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().cleanExpiredConsumerQueueByAddr(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack)
|
||||
throws RemotingException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getConsumerRunningInfo(consumerGroup, clientId, jstack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId,
|
||||
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().consumeMessageDirectly(consumerGroup, clientId, msgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MessageTrack> messageTrackDetail(MessageExt msg)
|
||||
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().messageTrackDetail(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cloneGroupOffset(String srcGroup, String destGroup, String topic, boolean isOffline)
|
||||
throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().cloneGroupOffset(srcGroup, destGroup, topic, isOffline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTopic(String key, String newTopic, int queueNum) throws MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createTopic(key, newTopic, queueNum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTopic(String key, String newTopic, int queueNum, int topicSysFlag)
|
||||
throws MQClientException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().createTopic(key, newTopic, queueNum, topicSysFlag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long searchOffset(MessageQueue mq, long timestamp) throws MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().searchOffset(mq, timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long maxOffset(MessageQueue mq) throws MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().maxOffset(mq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long minOffset(MessageQueue mq) throws MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().minOffset(mq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long earliestMsgStoreTime(MessageQueue mq) throws MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().earliestMsgStoreTime(mq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageExt viewMessage(String msgId)
|
||||
throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().viewMessage(msgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end)
|
||||
throws MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().queryMessage(topic, key, maxNum, begin, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void start() throws MQClientException {
|
||||
throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void shutdown() {
|
||||
throw new IllegalStateException("thisMethod is deprecated.use org.apache.rocketmq.console.aspect.admin.MQAdminAspect instead of this");
|
||||
}
|
||||
|
||||
// below is 3.2.6->3.5.8 updated
|
||||
|
||||
@Override
|
||||
public List<QueueTimeSpan> queryConsumeTimeSpan(String topic,
|
||||
String group) throws InterruptedException, MQBrokerException, RemotingException, MQClientException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().queryConsumeTimeSpan(topic, group);
|
||||
}
|
||||
|
||||
//MessageClientIDSetter.getNearlyTimeFromID has bug,so we subtract half a day
|
||||
//next version we will remove it
|
||||
//https://issues.apache.org/jira/browse/ROCKETMQ-111
|
||||
//https://github.com/apache/incubator-rocketmq/pull/69
|
||||
@Override
|
||||
public MessageExt viewMessage(String topic,
|
||||
String msgId) throws RemotingException, MQBrokerException, InterruptedException, MQClientException {
|
||||
logger.info("MessageClientIDSetter.getNearlyTimeFromID(msgId)={} msgId={}", MessageClientIDSetter.getNearlyTimeFromID(msgId), msgId);
|
||||
try {
|
||||
return viewMessage(msgId);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
MQAdminImpl mqAdminImpl = MQAdminInstance.threadLocalMqClientInstance().getMQAdminImpl();
|
||||
QueryResult qr = Reflect.on(mqAdminImpl).call("queryMessage", topic, msgId, 32,
|
||||
MessageClientIDSetter.getNearlyTimeFromID(msgId).getTime() - 1000 * 60 * 60 * 13L, Long.MAX_VALUE, true).get();
|
||||
if (qr != null && qr.getMessageList() != null && qr.getMessageList().size() > 0) {
|
||||
return qr.getMessageList().get(0);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeMessageDirectlyResult consumeMessageDirectly(String consumerGroup, String clientId, String topic,
|
||||
String msgId) throws RemotingException, MQClientException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().consumeMessageDirectly(consumerGroup, clientId, topic, msgId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Properties getBrokerConfig(
|
||||
String brokerAddr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, UnsupportedEncodingException, InterruptedException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getBrokerConfig(brokerAddr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicList fetchTopicsByCLuster(
|
||||
String clusterName) throws RemotingException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().fetchTopicsByCLuster(clusterName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanUnusedTopic(
|
||||
String cluster) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().cleanUnusedTopic(cluster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanUnusedTopicByAddr(
|
||||
String addr) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().cleanUnusedTopicByAddr(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrokerStatsData viewBrokerStatsData(String brokerAddr, String statsName,
|
||||
String statsKey) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().viewBrokerStatsData(brokerAddr, statsName, statsKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getClusterList(
|
||||
String topic) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getClusterList(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeStatsList fetchConsumeStatsInBroker(String brokerAddr, boolean isOrder,
|
||||
long timeoutMillis) throws RemotingConnectException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, InterruptedException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().fetchConsumeStatsInBroker(brokerAddr, isOrder, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getTopicClusterList(
|
||||
String topic) throws InterruptedException, MQBrokerException, MQClientException, RemotingException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getTopicClusterList(topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubscriptionGroupWrapper getAllSubscriptionGroup(String brokerAddr,
|
||||
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getAllSubscriptionGroup(brokerAddr, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicConfigSerializeWrapper getAllTopicGroup(String brokerAddr,
|
||||
long timeoutMillis) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQBrokerException {
|
||||
return MQAdminInstance.threadLocalMQAdminExt().getAllTopicGroup(brokerAddr, timeoutMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConsumeOffset(String brokerAddr, String consumeGroup, MessageQueue mq,
|
||||
long offset) throws RemotingException, InterruptedException, MQBrokerException {
|
||||
MQAdminInstance.threadLocalMQAdminExt().updateConsumeOffset(brokerAddr, consumeGroup, mq, offset);
|
||||
}
|
||||
|
||||
// 4.0.0 added
|
||||
@Override public void updateNameServerConfig(Properties properties,
|
||||
List<String> list) throws InterruptedException, RemotingConnectException, UnsupportedEncodingException, RemotingSendRequestException, RemotingTimeoutException, MQClientException, MQBrokerException {
|
||||
|
||||
}
|
||||
|
||||
@Override public Map<String, Properties> getNameServerConfig(
|
||||
List<String> list) throws InterruptedException, RemotingTimeoutException, RemotingSendRequestException, RemotingConnectException, MQClientException, UnsupportedEncodingException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.client;
|
||||
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.client.impl.MQClientAPIImpl;
|
||||
import org.apache.rocketmq.client.impl.factory.MQClientInstance;
|
||||
import org.apache.rocketmq.remoting.RemotingClient;
|
||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExt;
|
||||
import org.apache.rocketmq.tools.admin.DefaultMQAdminExtImpl;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.joor.Reflect;
|
||||
|
||||
public class MQAdminInstance {
|
||||
private static final ThreadLocal<DefaultMQAdminExt> MQ_ADMIN_EXT_THREAD_LOCAL = new ThreadLocal<DefaultMQAdminExt>();
|
||||
private static final ThreadLocal<Integer> INIT_COUNTER = new ThreadLocal<Integer>();
|
||||
|
||||
public static MQAdminExt threadLocalMQAdminExt() {
|
||||
DefaultMQAdminExt defaultMQAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
|
||||
if (defaultMQAdminExt == null) {
|
||||
throw new IllegalStateException("defaultMQAdminExt should be init before you get this");
|
||||
}
|
||||
return defaultMQAdminExt;
|
||||
}
|
||||
|
||||
public static RemotingClient threadLocalRemotingClient() {
|
||||
MQClientInstance mqClientInstance = threadLocalMqClientInstance();
|
||||
MQClientAPIImpl mQClientAPIImpl = Reflect.on(mqClientInstance).get("mQClientAPIImpl");
|
||||
return Reflect.on(mQClientAPIImpl).get("remotingClient");
|
||||
}
|
||||
|
||||
public static MQClientInstance threadLocalMqClientInstance() {
|
||||
DefaultMQAdminExtImpl defaultMQAdminExtImpl = Reflect.on(MQAdminInstance.threadLocalMQAdminExt()).get("defaultMQAdminExtImpl");
|
||||
return Reflect.on(defaultMQAdminExtImpl).get("mqClientInstance");
|
||||
}
|
||||
|
||||
public static void initMQAdminInstance(long timeoutMillis) throws MQClientException {
|
||||
Integer nowCount = INIT_COUNTER.get();
|
||||
if (nowCount == null) {
|
||||
DefaultMQAdminExt defaultMQAdminExt;
|
||||
if (timeoutMillis > 0) {
|
||||
defaultMQAdminExt = new DefaultMQAdminExt(timeoutMillis);
|
||||
}
|
||||
else {
|
||||
defaultMQAdminExt = new DefaultMQAdminExt();
|
||||
}
|
||||
defaultMQAdminExt.setInstanceName(Long.toString(System.currentTimeMillis()));
|
||||
defaultMQAdminExt.start();
|
||||
MQ_ADMIN_EXT_THREAD_LOCAL.set(defaultMQAdminExt);
|
||||
INIT_COUNTER.set(1);
|
||||
}
|
||||
else {
|
||||
INIT_COUNTER.set(nowCount + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void destroyMQAdminInstance() {
|
||||
Integer nowCount = INIT_COUNTER.get() - 1;
|
||||
if (nowCount > 0) {
|
||||
INIT_COUNTER.set(nowCount);
|
||||
return;
|
||||
}
|
||||
MQAdminExt mqAdminExt = MQ_ADMIN_EXT_THREAD_LOCAL.get();
|
||||
if (mqAdminExt != null) {
|
||||
mqAdminExt.shutdown();
|
||||
MQ_ADMIN_EXT_THREAD_LOCAL.remove();
|
||||
INIT_COUNTER.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.KVTable;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.apache.rocketmq.console.service.ClusterService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
@Service
|
||||
public class ClusterServiceImpl implements ClusterService {
|
||||
private Logger logger = LoggerFactory.getLogger(ClusterServiceImpl.class);
|
||||
@Resource
|
||||
private MQAdminExt mqAdminExt;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> list() {
|
||||
try {
|
||||
Map<String, Object> resultMap = Maps.newHashMap();
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
logger.info("op=look_clusterInfo {}", JsonUtil.obj2String(clusterInfo));
|
||||
Map<String/*brokerName*/, Map<Long/* brokerId */, Object/* brokerDetail */>> brokerServer = Maps.newHashMap();
|
||||
for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) {
|
||||
Map<Long, Object> brokerMasterSlaveMap = Maps.newHashMap();
|
||||
for (Map.Entry<Long/* brokerId */, String/* broker address */> brokerAddr : brokerData.getBrokerAddrs().entrySet()) {
|
||||
KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats(brokerAddr.getValue());
|
||||
// KVTable kvTable = mqAdminExt.fetchBrokerRuntimeStats("127.0.0.1:10911");
|
||||
brokerMasterSlaveMap.put(brokerAddr.getKey(), kvTable.getTable());
|
||||
}
|
||||
brokerServer.put(brokerData.getBrokerName(), brokerMasterSlaveMap);
|
||||
}
|
||||
resultMap.put("clusterInfo", clusterInfo);
|
||||
resultMap.put("brokerServer", brokerServer);
|
||||
return resultMap;
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Properties getBrokerConfig(String brokerAddr) {
|
||||
try {
|
||||
return mqAdminExt.getBrokerConfig(brokerAddr);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.exception.MQClientException;
|
||||
import org.apache.rocketmq.common.MQVersion;
|
||||
import org.apache.rocketmq.common.admin.ConsumeStats;
|
||||
import org.apache.rocketmq.common.admin.RollbackStats;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
import org.apache.rocketmq.common.protocol.ResponseCode;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.Connection;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerRunningInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.SubscriptionGroupWrapper;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.subscription.SubscriptionGroupConfig;
|
||||
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
|
||||
import org.apache.rocketmq.console.model.ConsumerGroupRollBackStat;
|
||||
import org.apache.rocketmq.console.model.GroupConsumeInfo;
|
||||
import org.apache.rocketmq.console.model.QueueStatInfo;
|
||||
import org.apache.rocketmq.console.model.TopicConsumerInfo;
|
||||
import org.apache.rocketmq.console.model.request.ConsumerConfigInfo;
|
||||
import org.apache.rocketmq.console.model.request.DeleteSubGroupRequest;
|
||||
import org.apache.rocketmq.console.model.request.ResetOffsetRequest;
|
||||
import org.apache.rocketmq.console.service.AbstractCommonService;
|
||||
import org.apache.rocketmq.console.service.ConsumerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import static com.google.common.base.Throwables.propagate;
|
||||
|
||||
@Service
|
||||
public class ConsumerServiceImpl extends AbstractCommonService implements ConsumerService {
|
||||
private Logger logger = LoggerFactory.getLogger(ConsumerServiceImpl.class);
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public List<GroupConsumeInfo> queryGroupList() {
|
||||
Set<String> consumerGroupSet = Sets.newHashSet();
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
for (BrokerData brokerData : clusterInfo.getBrokerAddrTable().values()) {
|
||||
SubscriptionGroupWrapper subscriptionGroupWrapper = mqAdminExt.getAllSubscriptionGroup(brokerData.selectBrokerAddr(), 3000L);
|
||||
consumerGroupSet.addAll(subscriptionGroupWrapper.getSubscriptionGroupTable().keySet());
|
||||
}
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
List<GroupConsumeInfo> groupConsumeInfoList = Lists.newArrayList();
|
||||
for (String consumerGroup : consumerGroupSet) {
|
||||
groupConsumeInfoList.add(queryGroup(consumerGroup));
|
||||
}
|
||||
Collections.sort(groupConsumeInfoList);
|
||||
return groupConsumeInfoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public GroupConsumeInfo queryGroup(String consumerGroup) {
|
||||
GroupConsumeInfo groupConsumeInfo = new GroupConsumeInfo();
|
||||
try {
|
||||
ConsumeStats consumeStats = null;
|
||||
try {
|
||||
consumeStats = mqAdminExt.examineConsumeStats(consumerGroup);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("examineConsumeStats exception, " + consumerGroup, e);
|
||||
}
|
||||
|
||||
ConsumerConnection consumerConnection = null;
|
||||
try {
|
||||
consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("examineConsumerConnectionInfo exception, " + consumerGroup, e);
|
||||
}
|
||||
|
||||
groupConsumeInfo.setGroup(consumerGroup);
|
||||
|
||||
if (consumeStats != null) {
|
||||
groupConsumeInfo.setConsumeTps((int)consumeStats.getConsumeTps());
|
||||
groupConsumeInfo.setDiffTotal(consumeStats.computeTotalDiff());
|
||||
}
|
||||
|
||||
if (consumerConnection != null) {
|
||||
groupConsumeInfo.setCount(consumerConnection.getConnectionSet().size());
|
||||
groupConsumeInfo.setMessageModel(consumerConnection.getMessageModel());
|
||||
groupConsumeInfo.setConsumeType(consumerConnection.getConsumeType());
|
||||
groupConsumeInfo.setVersion(MQVersion.getVersionDesc(consumerConnection.computeMinVersion()));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.warn("examineConsumeStats or examineConsumerConnectionInfo exception, "
|
||||
+ consumerGroup, e);
|
||||
}
|
||||
return groupConsumeInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TopicConsumerInfo> queryConsumeStatsListByGroupName(String groupName) {
|
||||
return queryConsumeStatsList(null, groupName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public List<TopicConsumerInfo> queryConsumeStatsList(final String topic, String groupName) {
|
||||
ConsumeStats consumeStats = null;
|
||||
try {
|
||||
consumeStats = mqAdminExt.examineConsumeStats(groupName, topic);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
List<MessageQueue> mqList = Lists.newArrayList(Iterables.filter(consumeStats.getOffsetTable().keySet(), new Predicate<MessageQueue>() {
|
||||
@Override
|
||||
public boolean apply(MessageQueue o) {
|
||||
return StringUtils.isBlank(topic) || o.getTopic().equals(topic);
|
||||
}
|
||||
}));
|
||||
Collections.sort(mqList);
|
||||
List<TopicConsumerInfo> topicConsumerInfoList = Lists.newArrayList();
|
||||
TopicConsumerInfo nowTopicConsumerInfo = null;
|
||||
Map<MessageQueue, String> messageQueueClientMap = getClientConnection(groupName);
|
||||
for (MessageQueue mq : mqList) {
|
||||
if (nowTopicConsumerInfo == null || (!StringUtils.equals(mq.getTopic(), nowTopicConsumerInfo.getTopic()))) {
|
||||
nowTopicConsumerInfo = new TopicConsumerInfo(mq.getTopic());
|
||||
topicConsumerInfoList.add(nowTopicConsumerInfo);
|
||||
}
|
||||
QueueStatInfo queueStatInfo = QueueStatInfo.fromOffsetTableEntry(mq, consumeStats.getOffsetTable().get(mq));
|
||||
queueStatInfo.setClientInfo(messageQueueClientMap.get(mq));
|
||||
nowTopicConsumerInfo.appendQueueStatInfo(queueStatInfo);
|
||||
}
|
||||
return topicConsumerInfoList;
|
||||
}
|
||||
|
||||
private Map<MessageQueue, String> getClientConnection(String groupName) {
|
||||
Map<MessageQueue, String> results = Maps.newHashMap();
|
||||
try {
|
||||
ConsumerConnection consumerConnection = mqAdminExt.examineConsumerConnectionInfo(groupName);
|
||||
for (Connection connection : consumerConnection.getConnectionSet()) {
|
||||
String clinetId = connection.getClientId();
|
||||
ConsumerRunningInfo consumerRunningInfo = mqAdminExt.getConsumerRunningInfo(groupName, clinetId, false);
|
||||
for (MessageQueue messageQueue : consumerRunningInfo.getMqTable().keySet()) {
|
||||
// results.put(messageQueue, clinetId + " " + connection.getClientAddr());
|
||||
results.put(messageQueue, clinetId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception err) {
|
||||
logger.error("op=getClientConnection_error", err);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public Map<String /*groupName*/, TopicConsumerInfo> queryConsumeStatsListByTopicName(String topic) {
|
||||
Map<String, TopicConsumerInfo> group2ConsumerInfoMap = Maps.newHashMap();
|
||||
try {
|
||||
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
|
||||
for (String group : groupList.getGroupList()) {
|
||||
List<TopicConsumerInfo> topicConsumerInfoList = null;
|
||||
try {
|
||||
topicConsumerInfoList = queryConsumeStatsList(topic, group);
|
||||
}
|
||||
catch (Exception ignore) {
|
||||
}
|
||||
group2ConsumerInfoMap.put(group, CollectionUtils.isEmpty(topicConsumerInfoList) ? new TopicConsumerInfo(topic) : topicConsumerInfoList.get(0));
|
||||
}
|
||||
return group2ConsumerInfoMap;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public Map<String, ConsumerGroupRollBackStat> resetOffset(ResetOffsetRequest resetOffsetRequest) {
|
||||
Map<String, ConsumerGroupRollBackStat> groupRollbackStats = Maps.newHashMap();
|
||||
for (String consumerGroup : resetOffsetRequest.getConsumerGroupList()) {
|
||||
try {
|
||||
Map<MessageQueue, Long> rollbackStatsMap =
|
||||
mqAdminExt.resetOffsetByTimestamp(resetOffsetRequest.getTopic(), consumerGroup, resetOffsetRequest.getResetTime(), resetOffsetRequest.isForce());
|
||||
ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true);
|
||||
List<RollbackStats> rollbackStatsList = consumerGroupRollBackStat.getRollbackStatsList();
|
||||
for (Map.Entry<MessageQueue, Long> rollbackStatsEntty : rollbackStatsMap.entrySet()) {
|
||||
RollbackStats rollbackStats = new RollbackStats();
|
||||
rollbackStats.setRollbackOffset(rollbackStatsEntty.getValue());
|
||||
rollbackStats.setQueueId(rollbackStatsEntty.getKey().getQueueId());
|
||||
rollbackStats.setBrokerName(rollbackStatsEntty.getKey().getBrokerName());
|
||||
rollbackStatsList.add(rollbackStats);
|
||||
}
|
||||
groupRollbackStats.put(consumerGroup, consumerGroupRollBackStat);
|
||||
}
|
||||
catch (MQClientException e) {
|
||||
if (ResponseCode.CONSUMER_NOT_ONLINE == e.getResponseCode()) {
|
||||
try {
|
||||
ConsumerGroupRollBackStat consumerGroupRollBackStat = new ConsumerGroupRollBackStat(true);
|
||||
List<RollbackStats> rollbackStatsList = mqAdminExt.resetOffsetByTimestampOld(consumerGroup, resetOffsetRequest.getTopic(), resetOffsetRequest.getResetTime(), true);
|
||||
consumerGroupRollBackStat.setRollbackStatsList(rollbackStatsList);
|
||||
groupRollbackStats.put(consumerGroup, consumerGroupRollBackStat);
|
||||
continue;
|
||||
}
|
||||
catch (Exception err) {
|
||||
logger.error("op=resetOffset_which_not_online_error", err);
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.error("op=resetOffset_error", e);
|
||||
}
|
||||
groupRollbackStats.put(consumerGroup, new ConsumerGroupRollBackStat(false, e.getMessage()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("op=resetOffset_error", e);
|
||||
groupRollbackStats.put(consumerGroup, new ConsumerGroupRollBackStat(false, e.getMessage()));
|
||||
}
|
||||
}
|
||||
return groupRollbackStats;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public List<ConsumerConfigInfo> examineSubscriptionGroupConfig(String group) {
|
||||
List<ConsumerConfigInfo> consumerConfigInfoList = Lists.newArrayList();
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
for (String brokerName : clusterInfo.getBrokerAddrTable().keySet()) { //foreach brokerName
|
||||
String brokerAddress = clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr();
|
||||
SubscriptionGroupConfig subscriptionGroupConfig = mqAdminExt.examineSubscriptionGroupConfig(brokerAddress, group);
|
||||
if (subscriptionGroupConfig == null) {
|
||||
continue;
|
||||
}
|
||||
consumerConfigInfoList.add(new ConsumerConfigInfo(Lists.newArrayList(brokerName), subscriptionGroupConfig));
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
return consumerConfigInfoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public boolean deleteSubGroup(DeleteSubGroupRequest deleteSubGroupRequest) {
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
for (String brokerName : deleteSubGroupRequest.getBrokerNameList()) {
|
||||
logger.info("addr={} groupName={}", clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
|
||||
mqAdminExt.deleteSubscriptionGroup(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), deleteSubGroupRequest.getGroupName());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw propagate(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean createAndUpdateSubscriptionGroupConfig(ConsumerConfigInfo consumerConfigInfo) {
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
|
||||
consumerConfigInfo.getClusterNameList(), consumerConfigInfo.getBrokerNameList())) {
|
||||
mqAdminExt.createAndUpdateSubscriptionGroupConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), consumerConfigInfo.getSubscriptionGroupConfig());
|
||||
}
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@MultiMQAdminCmdMethod
|
||||
public Set<String> fetchBrokerNameSetBySubscriptionGroup(String group) {
|
||||
Set<String> brokerNameSet = Sets.newHashSet();
|
||||
try {
|
||||
List<ConsumerConfigInfo> consumerConfigInfoList = examineSubscriptionGroupConfig(group);
|
||||
for (ConsumerConfigInfo consumerConfigInfo : consumerConfigInfoList) {
|
||||
brokerNameSet.addAll(consumerConfigInfo.getBrokerNameList());
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
return brokerNameSet;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerConnection getConsumerConnection(String consumerGroup) {
|
||||
try {
|
||||
return mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerRunningInfo getConsumerRunningInfo(String consumerGroup, String clientId, boolean jstack) {
|
||||
try {
|
||||
return mqAdminExt.getConsumerRunningInfo(consumerGroup, clientId, jstack);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.exception.ServiceException;
|
||||
import org.apache.rocketmq.console.service.DashboardCollectService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class DashboardCollectServiceImpl implements DashboardCollectService {
|
||||
|
||||
@Resource
|
||||
private RMQConfigure rmqConfigure;
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(DashboardCollectServiceImpl.class);
|
||||
|
||||
private LoadingCache<String, List<String>> brokerMap = CacheBuilder.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(10)
|
||||
.recordStats()
|
||||
.ticker(Ticker.systemTicker())
|
||||
.removalListener(new RemovalListener<Object, Object>() {
|
||||
@Override
|
||||
public void onRemoval(RemovalNotification<Object, Object> notification) {
|
||||
log.debug(notification.getKey() + " was removed, cause is " + notification.getCause());
|
||||
}
|
||||
})
|
||||
.build(
|
||||
new CacheLoader<String, List<String>>() {
|
||||
@Override
|
||||
public List<String> load(String key) {
|
||||
List<String> list = Lists.newArrayList();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
private LoadingCache<String, List<String>> topicMap = CacheBuilder.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.concurrencyLevel(10)
|
||||
.recordStats()
|
||||
.ticker(Ticker.systemTicker())
|
||||
.removalListener(new RemovalListener<Object, Object>() {
|
||||
@Override
|
||||
public void onRemoval(RemovalNotification<Object, Object> notification) {
|
||||
log.debug(notification.getKey() + " was removed, cause is " + notification.getCause());
|
||||
}
|
||||
})
|
||||
.build(
|
||||
new CacheLoader<String, List<String>>() {
|
||||
@Override
|
||||
public List<String> load(String key) {
|
||||
List<String> list = Lists.newArrayList();
|
||||
return list;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public LoadingCache<String, List<String>> getBrokerMap() {
|
||||
return brokerMap;
|
||||
}
|
||||
@Override
|
||||
public LoadingCache<String, List<String>> getTopicMap() {
|
||||
return topicMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> jsonDataFile2map(File file) {
|
||||
List<String> strings;
|
||||
try {
|
||||
strings = Files.readLines(file, Charsets.UTF_8);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (String string : strings) {
|
||||
sb.append(string);
|
||||
}
|
||||
JSONObject json = (JSONObject) JSONObject.parse(sb.toString());
|
||||
Set<Map.Entry<String, Object>> entries = json.entrySet();
|
||||
Map<String, List<String>> map = Maps.newHashMap();
|
||||
for (Map.Entry<String, Object> entry : entries) {
|
||||
JSONArray tpsArray = (JSONArray) entry.getValue();
|
||||
if (tpsArray == null) {
|
||||
continue;
|
||||
}
|
||||
Object[] tpsStrArray = tpsArray.toArray();
|
||||
List<String> tpsList = Lists.newArrayList();
|
||||
for (Object tpsObj : tpsStrArray) {
|
||||
tpsList.add("" + tpsObj);
|
||||
}
|
||||
map.put(entry.getKey(), tpsList);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getBrokerCache(String date) {
|
||||
String dataLocationPath = rmqConfigure.getConsoleCollectData();
|
||||
File file = new File(dataLocationPath + date + ".json");
|
||||
if (!file.exists()) {
|
||||
throw Throwables.propagate(new ServiceException(1, "This date have't data!"));
|
||||
}
|
||||
return jsonDataFile2map(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getTopicCache(String date) {
|
||||
String dataLocationPath = rmqConfigure.getConsoleCollectData();
|
||||
File file = new File(dataLocationPath + date + "_topic" + ".json");
|
||||
if (!file.exists()) {
|
||||
throw Throwables.propagate(new ServiceException(1, "This date have't data!"));
|
||||
}
|
||||
return jsonDataFile2map(file);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.service.DashboardCollectService;
|
||||
import org.apache.rocketmq.console.service.DashboardService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class DashboardServiceImpl implements DashboardService {
|
||||
|
||||
@Resource
|
||||
private DashboardCollectService dashboardCollectService;
|
||||
/**
|
||||
* @param date format yyyy-MM-dd
|
||||
*/
|
||||
@Override
|
||||
public Map<String, List<String>> queryBrokerData(String date) {
|
||||
return dashboardCollectService.getBrokerCache(date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> queryTopicData(String date) {
|
||||
return dashboardCollectService.getTopicCache(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date format yyyy-MM-dd
|
||||
* @param topicName
|
||||
*/
|
||||
@Override
|
||||
public List<String> queryTopicData(String date, String topicName) {
|
||||
if (null != dashboardCollectService.getTopicCache(date)) {
|
||||
return dashboardCollectService.getTopicCache(date).get(topicName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryTopicCurrentData() {
|
||||
Date date = new Date();
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Map<String, List<String>> topicCache = dashboardCollectService.getTopicCache(format.format(date));
|
||||
List<String> result = Lists.newArrayList();
|
||||
for (Map.Entry<String, List<String>> entry : topicCache.entrySet()) {
|
||||
List<String> value = entry.getValue();
|
||||
result.add(entry.getKey() + "," + value.get(value.size() - 1).split(",")[4]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
|
||||
import org.apache.rocketmq.client.consumer.PullResult;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.Pair;
|
||||
import org.apache.rocketmq.common.message.MessageExt;
|
||||
import org.apache.rocketmq.common.message.MessageQueue;
|
||||
import org.apache.rocketmq.common.protocol.body.Connection;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumeMessageDirectlyResult;
|
||||
import org.apache.rocketmq.common.protocol.body.ConsumerConnection;
|
||||
import org.apache.rocketmq.console.model.MessageView;
|
||||
import org.apache.rocketmq.console.service.MessageService;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.apache.rocketmq.tools.admin.api.MessageTrack;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MessageServiceImpl implements MessageService {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(MessageServiceImpl.class);
|
||||
/**
|
||||
* @see org.apache.rocketmq.store.config.MessageStoreConfig maxMsgsNumBatch = 64;
|
||||
* @see org.apache.rocketmq.store.index.IndexService maxNum = Math.min(maxNum, this.defaultMessageStore.getMessageStoreConfig().getMaxMsgsNumBatch());
|
||||
*/
|
||||
private final static int QUERY_MESSAGE_MAX_NUM = 64;
|
||||
@Resource
|
||||
private MQAdminExt mqAdminExt;
|
||||
|
||||
public Pair<MessageView, List<MessageTrack>> viewMessage(String subject, final String msgId) {
|
||||
try {
|
||||
|
||||
MessageExt messageExt = mqAdminExt.viewMessage(subject, msgId);
|
||||
List<MessageTrack> messageTrackList = messageTrackDetail(messageExt);
|
||||
return new Pair<>(MessageView.fromMessageExt(messageExt), messageTrackList);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MessageView> queryMessageByTopicAndKey(String topic, String key) {
|
||||
try {
|
||||
return Lists.transform(mqAdminExt.queryMessage(topic, key, QUERY_MESSAGE_MAX_NUM, 0, System.currentTimeMillis()).getMessageList(), new Function<MessageExt, MessageView>() {
|
||||
@Override
|
||||
public MessageView apply(MessageExt messageExt) {
|
||||
return MessageView.fromMessageExt(messageExt);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MessageView> queryMessageByTopic(String topic, final long begin, final long end) {
|
||||
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer(MixAll.TOOLS_CONSUMER_GROUP, null);
|
||||
List<MessageView> messageViewList = Lists.newArrayList();
|
||||
try {
|
||||
String subExpression = "*";
|
||||
consumer.start();
|
||||
Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues(topic);
|
||||
for (MessageQueue mq : mqs) {
|
||||
long minOffset = consumer.searchOffset(mq, begin);
|
||||
long maxOffset = consumer.searchOffset(mq, end);
|
||||
READQ:
|
||||
for (long offset = minOffset; offset <= maxOffset; ) {
|
||||
try {
|
||||
if (messageViewList.size() > 2000) {
|
||||
break;
|
||||
}
|
||||
PullResult pullResult = consumer.pull(mq, subExpression, offset, 32);
|
||||
offset = pullResult.getNextBeginOffset();
|
||||
switch (pullResult.getPullStatus()) {
|
||||
case FOUND:
|
||||
|
||||
List<MessageView> messageViewListByQuery = Lists.transform(pullResult.getMsgFoundList(), new Function<MessageExt, MessageView>() {
|
||||
@Override
|
||||
public MessageView apply(MessageExt messageExt) {
|
||||
messageExt.setBody(null);
|
||||
return MessageView.fromMessageExt(messageExt);
|
||||
}
|
||||
});
|
||||
List<MessageView> filteredList = Lists.newArrayList(Iterables.filter(messageViewListByQuery, new Predicate<MessageView>() {
|
||||
@Override
|
||||
public boolean apply(MessageView messageView) {
|
||||
if (messageView.getStoreTimestamp() < begin || messageView.getStoreTimestamp() > end) {
|
||||
logger.info("begin={} end={} time not in range {} {}", begin, end, messageView.getStoreTimestamp(), new Date(messageView.getStoreTimestamp()).toString());
|
||||
}
|
||||
return messageView.getStoreTimestamp() >= begin && messageView.getStoreTimestamp() <= end;
|
||||
}
|
||||
}));
|
||||
messageViewList.addAll(filteredList);
|
||||
break;
|
||||
case NO_MATCHED_MSG:
|
||||
case NO_NEW_MSG:
|
||||
case OFFSET_ILLEGAL:
|
||||
break READQ;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(messageViewList, new Comparator<MessageView>() {
|
||||
@Override
|
||||
public int compare(MessageView o1, MessageView o2) {
|
||||
if (o1.getStoreTimestamp() - o2.getStoreTimestamp() == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (o1.getStoreTimestamp() > o2.getStoreTimestamp()) ? -1 : 1;
|
||||
}
|
||||
});
|
||||
return messageViewList;
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
finally {
|
||||
consumer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MessageTrack> messageTrackDetail(MessageExt msg) {
|
||||
try {
|
||||
return mqAdminExt.messageTrackDetail(msg);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("op=messageTrackDetailError", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ConsumeMessageDirectlyResult consumeMessageDirectly(String topic, String msgId, String consumerGroup,
|
||||
String clientId) {
|
||||
if (StringUtils.isNotBlank(clientId)) {
|
||||
try {
|
||||
return mqAdminExt.consumeMessageDirectly(consumerGroup, clientId, topic, msgId);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ConsumerConnection consumerConnection = mqAdminExt.examineConsumerConnectionInfo(consumerGroup);
|
||||
for (Connection connection : consumerConnection.getConnectionSet()) {
|
||||
if (StringUtils.isBlank(connection.getClientId())) {
|
||||
continue;
|
||||
}
|
||||
logger.info("clientId={}", connection.getClientId());
|
||||
return mqAdminExt.consumeMessageDirectly(consumerGroup, connection.getClientId(), topic, msgId);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
throw new IllegalStateException("NO CONSUMER");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.google.common.base.Throwables;
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
|
||||
import org.apache.rocketmq.console.service.MonitorService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MonitorServiceImpl implements MonitorService {
|
||||
|
||||
|
||||
@Resource
|
||||
private RMQConfigure rmqConfigure;
|
||||
|
||||
private Map<String, ConsumerMonitorConfig> configMap = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public boolean createOrUpdateConsumerMonitor(String name, ConsumerMonitorConfig config) {
|
||||
configMap.put(name, config);// todo if write map success but write file fail
|
||||
writeToFile(getConsumerMonitorConfigDataPath(), configMap);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ConsumerMonitorConfig> queryConsumerMonitorConfig() {
|
||||
return configMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumerMonitorConfig queryConsumerMonitorConfigByGroupName(String consumeGroupName) {
|
||||
return configMap.get(consumeGroupName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteConsumerMonitor(String consumeGroupName) {
|
||||
configMap.remove(consumeGroupName);
|
||||
writeToFile(getConsumerMonitorConfigDataPath(), configMap);
|
||||
return true;
|
||||
}
|
||||
|
||||
//rocketmq.console.data.path/monitor/consumerMonitorConfig.json
|
||||
private String getConsumerMonitorConfigDataPath() {
|
||||
return rmqConfigure.getRocketMqConsoleDataPath() + File.separatorChar + "monitor" + File.separatorChar + "consumerMonitorConfig.json";
|
||||
}
|
||||
|
||||
private String getConsumerMonitorConfigDataPathBackUp() {
|
||||
return getConsumerMonitorConfigDataPath() + ".bak";
|
||||
}
|
||||
|
||||
private void writeToFile(String path, Object data) {
|
||||
writeDataJsonToFile(path, JsonUtil.obj2String(data));
|
||||
}
|
||||
|
||||
private void writeDataJsonToFile(String path, String dataStr) {
|
||||
try {
|
||||
MixAll.string2File(dataStr, path);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void loadData() {
|
||||
String content = MixAll.file2String(getConsumerMonitorConfigDataPath());
|
||||
if (content == null) {
|
||||
content = MixAll.file2String(getConsumerMonitorConfigDataPathBackUp());
|
||||
}
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
configMap = JsonUtil.string2Obj(content, new TypeReference<ConcurrentHashMap<String, ConsumerMonitorConfig>>() {
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Maps;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.service.AbstractCommonService;
|
||||
import org.apache.rocketmq.console.service.OpsService;
|
||||
import org.apache.rocketmq.console.service.checker.CheckerType;
|
||||
import org.apache.rocketmq.console.service.checker.RocketMqChecker;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class OpsServiceImpl extends AbstractCommonService implements OpsService {
|
||||
|
||||
@Resource
|
||||
private RMQConfigure rMQConfigure;
|
||||
|
||||
@Resource
|
||||
private List<RocketMqChecker> rocketMqCheckerList;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> homePageInfo() {
|
||||
Map<String, Object> homePageInfoMap = Maps.newHashMap();
|
||||
homePageInfoMap.put("namesvrAddrList", Splitter.on(";").splitToList(rMQConfigure.getNamesrvAddr()));
|
||||
homePageInfoMap.put("useVIPChannel", Boolean.valueOf(rMQConfigure.getIsVIPChannel()));
|
||||
return homePageInfoMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNameSvrAddrList(String nameSvrAddrList) {
|
||||
rMQConfigure.setNamesrvAddr(nameSvrAddrList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameSvrList() {
|
||||
return rMQConfigure.getNamesrvAddr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<CheckerType, Object> rocketMqStatusCheck() {
|
||||
Map<CheckerType, Object> checkResultMap = Maps.newHashMap();
|
||||
for (RocketMqChecker rocketMqChecker : rocketMqCheckerList) {
|
||||
checkResultMap.put(rocketMqChecker.checkerType(), rocketMqChecker.doCheck());
|
||||
}
|
||||
return checkResultMap;
|
||||
}
|
||||
|
||||
@Override public boolean updateIsVIPChannel(String useVIPChannel) {
|
||||
rMQConfigure.setIsVIPChannel(useVIPChannel);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.protocol.body.ProducerConnection;
|
||||
import org.apache.rocketmq.console.service.ProducerService;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ProducerServiceImpl implements ProducerService {
|
||||
@Resource
|
||||
private MQAdminExt mqAdminExt;
|
||||
|
||||
@Override
|
||||
public ProducerConnection getProducerConnection(String producerGroup, String topic) {
|
||||
try {
|
||||
return mqAdminExt.examineProducerConnectionInfo(producerGroup, topic);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.service.impl;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.rocketmq.client.producer.DefaultMQProducer;
|
||||
import org.apache.rocketmq.client.producer.SendResult;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.TopicConfig;
|
||||
import org.apache.rocketmq.common.admin.TopicStatsTable;
|
||||
import org.apache.rocketmq.common.message.Message;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.model.request.SendTopicMessageRequest;
|
||||
import org.apache.rocketmq.console.model.request.TopicConfigInfo;
|
||||
import org.apache.rocketmq.console.service.AbstractCommonService;
|
||||
import org.apache.rocketmq.console.service.TopicService;
|
||||
import org.apache.rocketmq.tools.command.CommandUtil;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class TopicServiceImpl extends AbstractCommonService implements TopicService {
|
||||
|
||||
@Autowired
|
||||
private RMQConfigure rMQConfigure;
|
||||
|
||||
@Override
|
||||
public TopicList fetchAllTopicList() {
|
||||
try {
|
||||
return mqAdminExt.fetchAllTopicList();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicStatsTable stats(String topic) {
|
||||
try {
|
||||
return mqAdminExt.examineTopicStats(topic);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicRouteData route(String topic) {
|
||||
try {
|
||||
return mqAdminExt.examineTopicRouteInfo(topic);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupList queryTopicConsumerInfo(String topic) {
|
||||
try {
|
||||
return mqAdminExt.queryTopicConsumeByWho(topic);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createOrUpdate(TopicConfigInfo topicCreateOrUpdateRequest) {
|
||||
TopicConfig topicConfig = new TopicConfig();
|
||||
BeanUtils.copyProperties(topicCreateOrUpdateRequest, topicConfig);
|
||||
try {
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
for (String brokerName : changeToBrokerNameSet(clusterInfo.getClusterAddrTable(),
|
||||
topicCreateOrUpdateRequest.getClusterNameList(), topicCreateOrUpdateRequest.getBrokerNameList())) {
|
||||
mqAdminExt.createAndUpdateTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topicConfig);
|
||||
}
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TopicConfig examineTopicConfig(String topic, String brokerName) {
|
||||
ClusterInfo clusterInfo = null;
|
||||
try {
|
||||
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
return mqAdminExt.examineTopicConfig(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr(), topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TopicConfigInfo> examineTopicConfig(String topic) {
|
||||
List<TopicConfigInfo> topicConfigInfoList = Lists.newArrayList();
|
||||
TopicRouteData topicRouteData = route(topic);
|
||||
for (BrokerData brokerData : topicRouteData.getBrokerDatas()) {
|
||||
TopicConfigInfo topicConfigInfo = new TopicConfigInfo();
|
||||
TopicConfig topicConfig = examineTopicConfig(topic, brokerData.getBrokerName());
|
||||
BeanUtils.copyProperties(topicConfig, topicConfigInfo);
|
||||
topicConfigInfo.setBrokerNameList(Lists.newArrayList(brokerData.getBrokerName()));
|
||||
topicConfigInfoList.add(topicConfigInfo);
|
||||
}
|
||||
return topicConfigInfoList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteTopic(String topic, String clusterName) {
|
||||
try {
|
||||
if (StringUtils.isBlank(clusterName)) {
|
||||
return deleteTopic(topic);
|
||||
}
|
||||
Set<String> masterSet = CommandUtil.fetchMasterAddrByClusterName(mqAdminExt, clusterName);
|
||||
mqAdminExt.deleteTopicInBroker(masterSet, topic);
|
||||
Set<String> nameServerSet = null;
|
||||
if (StringUtils.isNotBlank(rMQConfigure.getNamesrvAddr())) {
|
||||
String[] ns = rMQConfigure.getNamesrvAddr().split(";");
|
||||
nameServerSet = new HashSet<String>(Arrays.asList(ns));
|
||||
}
|
||||
mqAdminExt.deleteTopicInNameServer(nameServerSet, topic);
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteTopic(String topic) {
|
||||
ClusterInfo clusterInfo = null;
|
||||
try {
|
||||
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
for (String clusterName : clusterInfo.getClusterAddrTable().keySet()) {
|
||||
deleteTopic(topic, clusterName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteTopicInBroker(String brokerName, String topic) {
|
||||
|
||||
try {
|
||||
ClusterInfo clusterInfo = null;
|
||||
try {
|
||||
clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
mqAdminExt.deleteTopicInBroker(Sets.newHashSet(clusterInfo.getBrokerAddrTable().get(brokerName).selectBrokerAddr()), topic);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendResult sendTopicMessageRequest(SendTopicMessageRequest sendTopicMessageRequest) {
|
||||
DefaultMQProducer producer = new DefaultMQProducer(MixAll.SELF_TEST_PRODUCER_GROUP);
|
||||
producer.setInstanceName(String.valueOf(System.currentTimeMillis()));
|
||||
producer.setNamesrvAddr(rMQConfigure.getNamesrvAddr());
|
||||
try {
|
||||
producer.start();
|
||||
Message msg = new Message(sendTopicMessageRequest.getTopic(),
|
||||
sendTopicMessageRequest.getTag(),
|
||||
sendTopicMessageRequest.getKey(),
|
||||
sendTopicMessageRequest.getMessageBody().getBytes()
|
||||
);
|
||||
return producer.send(msg);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
finally {
|
||||
producer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.support;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.rocketmq.console.exception.ServiceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
@ControllerAdvice(basePackages = "org.apache.rocketmq.console")
|
||||
public class GlobalExceptionHandler {
|
||||
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
@ResponseBody
|
||||
public JsonResult<Object> jsonErrorHandler(HttpServletRequest req, Exception ex) throws Exception {
|
||||
JsonResult<Object> value = null;
|
||||
if (ex != null) {
|
||||
logger.error("op=global_exception_handler_print_error", ex);
|
||||
if (ex instanceof ServiceException) {
|
||||
value = new JsonResult<Object>(((ServiceException) ex).getCode(), ex.getMessage());
|
||||
}
|
||||
else {
|
||||
value = new JsonResult<Object>(-1, ex.getMessage());
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.support;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import org.apache.rocketmq.console.aspect.admin.annotation.OriginalControllerReturnValue;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
|
||||
@ControllerAdvice(basePackages = "org.apache.rocketmq.console")
|
||||
public class GlobalRestfulResponseBodyAdvice implements ResponseBodyAdvice<Object> {
|
||||
|
||||
private Logger logger = LoggerFactory.getLogger(GlobalRestfulResponseBodyAdvice.class);
|
||||
|
||||
@Override
|
||||
public Object beforeBodyWrite(
|
||||
Object obj, MethodParameter methodParameter, MediaType mediaType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType,
|
||||
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
|
||||
Annotation originalControllerReturnValue = methodParameter.getMethodAnnotation(OriginalControllerReturnValue.class);
|
||||
if (originalControllerReturnValue != null) {
|
||||
return obj;
|
||||
}
|
||||
JsonResult value;
|
||||
if (obj instanceof JsonResult) {
|
||||
value = (JsonResult)obj;
|
||||
}
|
||||
else {
|
||||
value = new JsonResult(obj);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.support;
|
||||
|
||||
public class JsonResult<T> {
|
||||
private int status = 0;
|
||||
private T data;
|
||||
private String errMsg;
|
||||
|
||||
public JsonResult(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public JsonResult(int status, String errMsg) {
|
||||
this.status = status;
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
|
||||
public JsonResult(int status, T data, String errMsg) {
|
||||
this.status = status;
|
||||
this.data = data;
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getErrMsg() {
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
public void setErrMsg(String errMsg) {
|
||||
this.errMsg = errMsg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.task;
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import org.apache.rocketmq.common.protocol.body.ClusterInfo;
|
||||
import org.apache.rocketmq.common.protocol.body.GroupList;
|
||||
import org.apache.rocketmq.common.protocol.body.KVTable;
|
||||
import org.apache.rocketmq.common.protocol.body.TopicList;
|
||||
import org.apache.rocketmq.common.protocol.route.BrokerData;
|
||||
import org.apache.rocketmq.common.protocol.route.TopicRouteData;
|
||||
import org.apache.rocketmq.console.aspect.admin.annotation.MultiMQAdminCmdMethod;
|
||||
import org.apache.rocketmq.store.stats.BrokerStatsManager;
|
||||
import org.apache.rocketmq.tools.admin.MQAdminExt;
|
||||
import org.apache.rocketmq.tools.command.stats.StatsAllSubCommand;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.common.MixAll;
|
||||
import org.apache.rocketmq.common.protocol.body.BrokerStatsData;
|
||||
import org.apache.rocketmq.console.config.RMQConfigure;
|
||||
import org.apache.rocketmq.console.service.DashboardCollectService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class DashboardCollectTask {
|
||||
private Date currentDate = new Date();
|
||||
@Resource
|
||||
private MQAdminExt mqAdminExt;
|
||||
@Resource
|
||||
private RMQConfigure rmqConfigure;
|
||||
|
||||
@Resource
|
||||
private DashboardCollectService dashboardCollectService;
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(DashboardCollectTask.class);
|
||||
|
||||
@Scheduled(cron = "30 0/1 * * * ?")
|
||||
@MultiMQAdminCmdMethod(timeoutMillis = 5000)
|
||||
public void collectTopic() {
|
||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||
return;
|
||||
}
|
||||
Date date = new Date();
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
try {
|
||||
TopicList topicList = mqAdminExt.fetchAllTopicList();
|
||||
Set<String> topicSet = topicList.getTopicList();
|
||||
for (String topic : topicSet) {
|
||||
if (topic.startsWith(MixAll.RETRY_GROUP_TOPIC_PREFIX) || topic.startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TopicRouteData topicRouteData = mqAdminExt.examineTopicRouteInfo(topic);
|
||||
|
||||
GroupList groupList = mqAdminExt.queryTopicConsumeByWho(topic);
|
||||
|
||||
double inTPS = 0;
|
||||
|
||||
long inMsgCntToday = 0;
|
||||
|
||||
double outTPS = 0;
|
||||
|
||||
long outMsgCntToday = 0;
|
||||
|
||||
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
|
||||
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
if (masterAddr != null) {
|
||||
try {
|
||||
stopwatch.start();
|
||||
log.info("start time: {}", stopwatch.toString());
|
||||
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.TOPIC_PUT_NUMS, topic);
|
||||
stopwatch.stop();
|
||||
log.info("stop time : {}", stopwatch.toString());
|
||||
stopwatch.reset();
|
||||
inTPS += bsd.getStatsMinute().getTps();
|
||||
inMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (groupList != null && !groupList.getGroupList().isEmpty()) {
|
||||
|
||||
for (String group : groupList.getGroupList()) {
|
||||
for (BrokerData bd : topicRouteData.getBrokerDatas()) {
|
||||
String masterAddr = bd.getBrokerAddrs().get(MixAll.MASTER_ID);
|
||||
if (masterAddr != null) {
|
||||
try {
|
||||
String statsKey = String.format("%s@%s", topic, group);
|
||||
BrokerStatsData bsd = mqAdminExt.viewBrokerStatsData(masterAddr, BrokerStatsManager.GROUP_GET_NUMS, statsKey);
|
||||
outTPS += bsd.getStatsMinute().getTps();
|
||||
outMsgCntToday += StatsAllSubCommand.compute24HourSum(bsd);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> list;
|
||||
try {
|
||||
list = dashboardCollectService.getTopicMap().get(topic);
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
if (null == list) {
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
|
||||
list.add(date.getTime() + "," + new BigDecimal(inTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + inMsgCntToday + "," + new BigDecimal(outTPS).setScale(5, BigDecimal.ROUND_HALF_UP) + "," + outMsgCntToday);
|
||||
dashboardCollectService.getTopicMap().put(topic, list);
|
||||
|
||||
}
|
||||
|
||||
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
|
||||
}
|
||||
catch (Exception err) {
|
||||
throw Throwables.propagate(err);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 0/1 * * * ?")
|
||||
public void collectBroker() {
|
||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Date date = new Date();
|
||||
ClusterInfo clusterInfo = mqAdminExt.examineBrokerClusterInfo();
|
||||
Set<Map.Entry<String, BrokerData>> clusterEntries = clusterInfo.getBrokerAddrTable().entrySet();
|
||||
|
||||
Map<String, String> addresses = Maps.newHashMap();
|
||||
for (Map.Entry<String, BrokerData> clusterEntry : clusterEntries) {
|
||||
HashMap<Long, String> addrs = clusterEntry.getValue().getBrokerAddrs();
|
||||
Set<Map.Entry<Long, String>> addrsEntries = addrs.entrySet();
|
||||
for (Map.Entry<Long, String> addrEntry : addrsEntries) {
|
||||
addresses.put(addrEntry.getValue(), clusterEntry.getKey() + ":" + addrEntry.getKey());
|
||||
}
|
||||
}
|
||||
Set<Map.Entry<String, String>> entries = addresses.entrySet();
|
||||
for (Map.Entry<String, String> entry : entries) {
|
||||
List<String> list = dashboardCollectService.getBrokerMap().get(entry.getValue());
|
||||
if (null == list) {
|
||||
list = Lists.newArrayList();
|
||||
}
|
||||
KVTable kvTable = fetchBrokerRuntimeStats(entry.getKey(), 3);
|
||||
if (kvTable == null) {
|
||||
continue;
|
||||
}
|
||||
String[] tpsArray = kvTable.getTable().get("getTotalTps").split(" ");
|
||||
BigDecimal totalTps = new BigDecimal(0);
|
||||
for (String tps : tpsArray) {
|
||||
totalTps = totalTps.add(new BigDecimal(tps));
|
||||
}
|
||||
BigDecimal averageTps = totalTps.divide(new BigDecimal(tpsArray.length), 5, BigDecimal.ROUND_HALF_UP);
|
||||
list.add(date.getTime() + "," + averageTps.toString());
|
||||
dashboardCollectService.getBrokerMap().put(entry.getValue(), list);
|
||||
}
|
||||
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
private KVTable fetchBrokerRuntimeStats(String brokerAddr, Integer retryTime) {
|
||||
if (retryTime == 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return mqAdminExt.fetchBrokerRuntimeStats(brokerAddr);
|
||||
}
|
||||
catch (Exception e) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
catch (InterruptedException e1) {
|
||||
throw Throwables.propagate(e1);
|
||||
}
|
||||
fetchBrokerRuntimeStats(brokerAddr, retryTime - 1);
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0/5 * * * * ?")
|
||||
public void saveData() {
|
||||
if (!rmqConfigure.isEnableDashBoardCollect()) {
|
||||
return;
|
||||
}
|
||||
//one day refresh cache one time
|
||||
String dataLocationPath = rmqConfigure.getConsoleCollectData();
|
||||
DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
|
||||
String nowDateStr = format.format(new Date());
|
||||
String currentDateStr = format.format(currentDate);
|
||||
if (!currentDateStr.equals(nowDateStr)) {
|
||||
dashboardCollectService.getBrokerMap().invalidateAll();
|
||||
dashboardCollectService.getTopicMap().invalidateAll();
|
||||
currentDate = new Date();
|
||||
}
|
||||
File brokerFile = new File(dataLocationPath + nowDateStr + ".json");
|
||||
File topicFile = new File(dataLocationPath + nowDateStr + "_topic" + ".json");
|
||||
try {
|
||||
Map<String, List<String>> brokerFileMap;
|
||||
Map<String, List<String>> topicFileMap;
|
||||
if (brokerFile.exists()) {
|
||||
brokerFileMap = dashboardCollectService.jsonDataFile2map(brokerFile);
|
||||
}
|
||||
else {
|
||||
brokerFileMap = Maps.newHashMap();
|
||||
Files.createParentDirs(brokerFile);
|
||||
}
|
||||
|
||||
if (topicFile.exists()) {
|
||||
topicFileMap = dashboardCollectService.jsonDataFile2map(topicFile);
|
||||
}
|
||||
else {
|
||||
topicFileMap = Maps.newHashMap();
|
||||
Files.createParentDirs(topicFile);
|
||||
}
|
||||
|
||||
brokerFile.createNewFile();
|
||||
topicFile.createNewFile();
|
||||
|
||||
writeFile(dashboardCollectService.getBrokerMap(), brokerFileMap, brokerFile);
|
||||
writeFile(dashboardCollectService.getTopicMap(), topicFileMap, topicFile);
|
||||
log.debug("Broker Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getBrokerMap().asMap()));
|
||||
log.debug("Topic Collected Data in memory = {}" + JsonUtil.obj2String(dashboardCollectService.getTopicMap().asMap()));
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw Throwables.propagate(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFile(LoadingCache<String, List<String>> map, Map<String, List<String>> fileMap,
|
||||
File file) throws IOException {
|
||||
Map<String, List<String>> newMap = map.asMap();
|
||||
Map<String, List<String>> resultMap = Maps.newHashMap();
|
||||
if (fileMap.size() == 0) {
|
||||
resultMap = newMap;
|
||||
}
|
||||
else {
|
||||
for (Map.Entry<String, List<String>> entry : fileMap.entrySet()) {
|
||||
List<String> oldList = entry.getValue();
|
||||
List<String> newList = newMap.get(entry.getKey());
|
||||
resultMap.put(entry.getKey(), appendData(newList, oldList));
|
||||
if (newList == null || newList.size() == 0) {
|
||||
map.put(entry.getKey(), appendData(newList, oldList));
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : newMap.entrySet()) {
|
||||
List<String> oldList = fileMap.get(entry.getKey());
|
||||
if (oldList == null || oldList.size() == 0) {
|
||||
resultMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
Files.write(JsonUtil.obj2String(resultMap).getBytes(), file);
|
||||
}
|
||||
|
||||
private List<String> appendData(List<String> newTpsList, List<String> oldTpsList) {
|
||||
List<String> result = Lists.newArrayList();
|
||||
if (newTpsList == null || newTpsList.size() == 0) {
|
||||
return oldTpsList;
|
||||
}
|
||||
if (oldTpsList == null || oldTpsList.size() == 0) {
|
||||
return newTpsList;
|
||||
}
|
||||
String oldLastTps = oldTpsList.get(oldTpsList.size() - 1);
|
||||
Long oldLastTimestamp = Long.parseLong(oldLastTps.split(",")[0]);
|
||||
String newFirstTps = newTpsList.get(0);
|
||||
Long newFirstTimestamp = Long.parseLong(newFirstTps.split(",")[0]);
|
||||
if (oldLastTimestamp.longValue() < newFirstTimestamp.longValue()) {
|
||||
result.addAll(oldTpsList);
|
||||
result.addAll(newTpsList);
|
||||
return result;
|
||||
}
|
||||
return newTpsList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.rocketmq.console.task;
|
||||
|
||||
import java.util.Map;
|
||||
import javax.annotation.Resource;
|
||||
import org.apache.rocketmq.console.model.ConsumerMonitorConfig;
|
||||
import org.apache.rocketmq.console.model.GroupConsumeInfo;
|
||||
import org.apache.rocketmq.console.service.ConsumerService;
|
||||
import org.apache.rocketmq.console.service.MonitorService;
|
||||
import org.apache.rocketmq.console.util.JsonUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class MonitorTask {
|
||||
private Logger logger = LoggerFactory.getLogger(MonitorTask.class);
|
||||
|
||||
@Resource
|
||||
private MonitorService monitorService;
|
||||
|
||||
@Resource
|
||||
private ConsumerService consumerService;
|
||||
|
||||
// @Scheduled(cron = "* * * * * ?")
|
||||
public void scanProblemConsumeGroup() {
|
||||
for (Map.Entry<String, ConsumerMonitorConfig> configEntry : monitorService.queryConsumerMonitorConfig().entrySet()) {
|
||||
GroupConsumeInfo consumeInfo = consumerService.queryGroup(configEntry.getKey());
|
||||
if (consumeInfo.getCount() < configEntry.getValue().getMinCount() || consumeInfo.getDiffTotal() > configEntry.getValue().getMaxDiffTotal()) {
|
||||
logger.info("op=look consumeInfo {}", JsonUtil.obj2String(consumeInfo)); // notify the alert system
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
156
src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
Normal file
156
src/main/java/org/apache/rocketmq/console/util/JsonUtil.java
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.rocketmq.console.util;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class JsonUtil {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(JsonUtil.class);
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private JsonUtil() {
|
||||
}
|
||||
|
||||
static {
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
|
||||
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
|
||||
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||
objectMapper.setFilters(new SimpleFilterProvider().setFailOnUnknownId(false));
|
||||
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
|
||||
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
public static void writeValue(Writer writer, Object obj) {
|
||||
try {
|
||||
objectMapper.writeValue(writer, obj);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Throwables.propagateIfPossible(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> String obj2String(T src) {
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return src instanceof String ? (String)src : objectMapper.writeValueAsString(src);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Parse Object to String error src=" + src, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> byte[] obj2Byte(T src) {
|
||||
if (src == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return src instanceof byte[] ? (byte[])src : objectMapper.writeValueAsBytes(src);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Parse Object to byte[] error", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T string2Obj(String str, Class<T> clazz) {
|
||||
if (Strings.isNullOrEmpty(str) || clazz == null) {
|
||||
return null;
|
||||
}
|
||||
str = escapesSpecialChar(str);
|
||||
try {
|
||||
return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Parse String to Object error\nString: {}\nClass<T>: {}\nError: {}", str, clazz.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T byte2Obj(byte[] bytes, Class<T> clazz) {
|
||||
if (bytes == null || clazz == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return clazz.equals(byte[].class) ? (T)bytes : objectMapper.readValue(bytes, clazz);
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Parse byte[] to Object error\nbyte[]: {}\nClass<T>: {}\nError: {}", bytes, clazz.getName(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T string2Obj(String str, TypeReference<T> typeReference) {
|
||||
if (Strings.isNullOrEmpty(str) || typeReference == null) {
|
||||
return null;
|
||||
}
|
||||
str = escapesSpecialChar(str);
|
||||
try {
|
||||
return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference));
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Parse String to Object error\nString: {}\nTypeReference<T>: {}\nError: {}", str,
|
||||
typeReference.getType(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T byte2Obj(byte[] bytes, TypeReference<T> typeReference) {
|
||||
if (bytes == null || typeReference == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (T)(typeReference.getType().equals(byte[].class) ? bytes : objectMapper.readValue(bytes,
|
||||
typeReference));
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("Parse byte[] to Object error\nbyte[]: {}\nTypeReference<T>: {}\nError: {}", bytes,
|
||||
typeReference.getType(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T map2Obj(Map<String, String> map, Class<T> clazz) {
|
||||
String str = obj2String(map);
|
||||
return string2Obj(str, clazz);
|
||||
}
|
||||
|
||||
private static String escapesSpecialChar(String str) {
|
||||
return str.replace("\n", "\\n").replace("\r", "\\r");
|
||||
}
|
||||
}
|
||||
16
src/main/resources/application.properties
Normal file
16
src/main/resources/application.properties
Normal file
@@ -0,0 +1,16 @@
|
||||
server.contextPath=
|
||||
server.port=8080
|
||||
#spring.application.index=true
|
||||
spring.application.name=rocketmq-console
|
||||
spring.http.encoding.charset=UTF-8
|
||||
spring.http.encoding.enabled=true
|
||||
spring.http.encoding.force=true
|
||||
logging.config=classpath:logback.xml
|
||||
#if this value is empty,use env value rocketmq.config.namesrvAddr NAMESRV_ADDR | now, you can set it in ops page.default localhost:9876
|
||||
rocketmq.config.namesrvAddr=
|
||||
#if you use rocketmq version < 3.5.8, rocketmq.config.isVIPChannel should be false.default true
|
||||
rocketmq.config.isVIPChannel=
|
||||
#rocketmq-console's data path:dashboard/monitor
|
||||
rocketmq.config.dataPath=/tmp/rocketmq-console/data
|
||||
#set it false if you don't want use dashboard.default true
|
||||
rocketmq.config.enableDashBoardCollect=true
|
||||
33
src/main/resources/logback.xml
Normal file
33
src/main/resources/logback.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder charset="UTF-8">
|
||||
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE"
|
||||
class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${user.home}/logs/consolelogs/rocketmq-console.log</file>
|
||||
<append>true</append>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${user.home}/logs/consolelogs/rocketmq-console-%d{yyyy-MM-dd}.%i.log
|
||||
</fileNamePattern>
|
||||
<timeBasedFileNamingAndTriggeringPolicy
|
||||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
|
||||
<maxFileSize>104857600</maxFileSize>
|
||||
</timeBasedFileNamingAndTriggeringPolicy>
|
||||
<MaxHistory>10</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %5p %m%n</pattern>
|
||||
<charset class="java.nio.charset.Charset">UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
117
src/main/resources/static/index.html
Normal file
117
src/main/resources/static/index.html
Normal file
@@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<html lang="en" ng-app="app">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>RocketMq-console-ng</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name='description' content=''>
|
||||
<meta name='keywords' content=''>
|
||||
<!--iOS -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
|
||||
<!-- preLoading -->
|
||||
<link rel="stylesheet" href="style/preLoading/normalize.css">
|
||||
<link rel="stylesheet" href="style/preLoading/main.css">
|
||||
<script src="vendor/preLoading/modernizr-2.6.2.min.js"></script>
|
||||
<!-- preLoading -->
|
||||
|
||||
<link rel="stylesheet" href="vendor/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="vendor/bootstrap-material-design/css/bootstrap-material-design.css">
|
||||
<link rel="stylesheet" href="vendor/bootstrap-material-design/css/ripples.css">
|
||||
<link rel="stylesheet" href="vendor/angular/notification/angular-ui-notification.css">
|
||||
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog.min.css">
|
||||
<link rel="stylesheet" href="vendor/ng-dialog/ngDialog-theme-default.css">
|
||||
<link rel="stylesheet" href="vendor/dropdown/jquery.dropdown.css">
|
||||
<link rel="stylesheet" href="vendor/datatimepicker/bootstrap-datetimepicker.min.css">
|
||||
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/css/font-awesome.min.css">
|
||||
<link rel="stylesheet" href="vendor/font-awesome-4.7.0/fonts/fontawesome-webfont.svg">
|
||||
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="vendor/chosen/chosen-spinner.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="vendor/angular-material/angular-material.min.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="vendor/md-tab/docs.css"/>
|
||||
<link rel="stylesheet" href="style/animate.css">
|
||||
<link rel="stylesheet" href="style/theme.css">
|
||||
<link rel="stylesheet" href="style/app.css">
|
||||
|
||||
</head>
|
||||
<body ng-controller="AppCtrl">
|
||||
<!--[if lte IE 9]>
|
||||
<script type="text/javascript">
|
||||
location.href = 'view/pages/un_support_browser.html';
|
||||
</script>
|
||||
<![endif]-->
|
||||
|
||||
<div id="loader-wrapper">
|
||||
<div id="loader"></div>
|
||||
|
||||
<div class="loader-section section-left"></div>
|
||||
<div class="loader-section section-right"></div>
|
||||
|
||||
</div>
|
||||
<div ng-include="'view/layout/_header.html'"></div>
|
||||
<section ng-view></section>
|
||||
<div ng-include="'view/layout/_footer.html'"></div>
|
||||
<script type="text/javascript" src="vendor/jquery/jquery1.11.3.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/bootstrap-material-design/js/material.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/bootstrap-material-design/js/ripples.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-translate.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="vendor/angular/angular-translate-storage-cookie/angular-translate-storage-cookie.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/i18n/angular-locale_zh-cn.js"></script>
|
||||
<script type="text/javascript" src="src/i18n/en.js"></script>
|
||||
<script type="text/javascript" src="src/i18n/zh.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-cookies.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-animate.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-route.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-ui-router.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-sanitize.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-aria.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-messages.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/angular-sanitize.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/angular/notification/angular-ui-notification.js"></script>
|
||||
<script type="text/javascript" src="vendor/pagination/tm.pagination.js"></script>
|
||||
<script type="text/javascript" src="vendor/ng-dialog/ngDialog.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/json-bigint/json-bigint.js"></script>
|
||||
<script type="text/javascript" src="vendor/dropdown/jquery.dropdown.js"></script>
|
||||
<script type="text/javascript" src="vendor/datatimepicker/moment.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/datatimepicker/bootstrap-datetimepicker.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/datatimepicker/angular-eonasdan-datetimepicker.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/chosen/angular-chosen.js"></script>
|
||||
<script type="text/javascript" src="vendor/chosen/chosen.jquery.min.js"></script>
|
||||
<script type="text/javascript" src='vendor/md-tab/svg-assets-cache.js'></script>
|
||||
<script type="text/javascript" src='vendor/angular-material/angular-material.min.js'></script>
|
||||
<script type="text/javascript" src="vendor/echarts/echarts.min.js"></script>
|
||||
<script type="text/javascript" src="src/app.js"></script>
|
||||
<script type="text/javascript" src="src/controller.js?v=201702250025"></script>
|
||||
<script type="text/javascript" src="src/tools/tools.js"></script>
|
||||
<script type="text/javascript" src="src/cluster.js?timestamp=4"></script>
|
||||
<script type="text/javascript" src="src/topic.js"></script>
|
||||
<script type="text/javascript" src="src/consumer.js?timestamp=6"></script>
|
||||
<script type="text/javascript" src="src/producer.js"></script>
|
||||
<script type="text/javascript" src="src/message.js"></script>
|
||||
<script type="text/javascript" src="src/ops.js?timestamp=7"></script>
|
||||
<script type="text/javascript" src="src/remoteApi/remoteApi.js"></script>
|
||||
<script type="text/javascript" src="vendor/preLoading/main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
208
src/main/resources/static/src/app.js
Normal file
208
src/main/resources/static/src/app.js
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
var app = angular.module('app', [
|
||||
'ngAnimate',
|
||||
'ngCookies',
|
||||
'ngRoute',
|
||||
'ngDialog',
|
||||
'ngMaterial',
|
||||
'ngSanitize',
|
||||
'material.svgAssetsCache',
|
||||
'ui-notification',
|
||||
'tm.pagination',
|
||||
'ae-datetimepicker',
|
||||
'localytics.directives',
|
||||
'pascalprecht.translate'
|
||||
]).run(
|
||||
['$rootScope','$location','$cookies',
|
||||
function ($rootScope,$location,$cookies) {
|
||||
// var filter = function(url){
|
||||
// var outFilterArrs = []
|
||||
// outFilterArrs.push("/login");
|
||||
// outFilterArrs.push("/reg");
|
||||
// outFilterArrs.push("/logout");
|
||||
// outFilterArrs.push("/404");
|
||||
// var flag = false;
|
||||
// $.each(outFilterArrs,function(i,value){
|
||||
// if(url.indexOf(value) > -1){
|
||||
// flag = true;
|
||||
// return false;
|
||||
// }
|
||||
// });
|
||||
// return flag;
|
||||
// }
|
||||
|
||||
// if(angular.isDefined($cookies.get("isLogin")) && $cookies.get("isLogin") == 'true'){
|
||||
// chatApi.login();
|
||||
// }
|
||||
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
var pathArray = $location.url().split("/");
|
||||
var index = pathArray.indexOf("");
|
||||
if(index >= 0){
|
||||
pathArray.remove(index);
|
||||
}
|
||||
$rootScope.path = pathArray[0];
|
||||
|
||||
//初始化material UI控件
|
||||
$.material.init();
|
||||
});
|
||||
|
||||
$rootScope.$on('$routeChangeStart',function (evt, next,current) {
|
||||
window.clearInterval($rootScope._thread);
|
||||
})
|
||||
}
|
||||
]
|
||||
).animation('.view', function () {
|
||||
return {
|
||||
animate: function (element, className, from, to, done) {
|
||||
//styles
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.provider('getDictName', function () {
|
||||
|
||||
var dictList = [];
|
||||
|
||||
this.init = function () {
|
||||
var url = "src/data/dict.json";//无法使用common服务类,地址只能写死
|
||||
var params = {};
|
||||
$.get(url, params, function (ret) {
|
||||
dictList = ret;
|
||||
})
|
||||
}
|
||||
|
||||
this.$get = function () {
|
||||
return function (dictType, value) {
|
||||
for (var i = 0; i < dictList.length; i++) {
|
||||
var dict = dictList[i];
|
||||
if (dict.TYPE == dictType && dict.DICT_VALUE == value) {
|
||||
return dict.DICT_NAME;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.config(['$routeProvider', '$httpProvider','$cookiesProvider','getDictNameProvider','$sceProvider','$translateProvider','$mdThemingProvider',
|
||||
function ($routeProvider, $httpProvider ,$cookiesProvider,getDictNameProvider,$sceProvider,$translateProvider,$mdThemingProvider) {
|
||||
//关闭html校验,存在安全隐患,但目前没问题,使用ng-bind-html需要注意,防止跨站攻击
|
||||
$sceProvider.enabled(false);
|
||||
//前端字典项目初始化
|
||||
getDictNameProvider.init();
|
||||
|
||||
//init angular
|
||||
$mdThemingProvider.theme('default')
|
||||
.primaryPalette('pink')
|
||||
.accentPalette('light-blue');
|
||||
|
||||
|
||||
//设置ajax默认配置
|
||||
$.ajaxSetup({
|
||||
type: "POST",
|
||||
contentType: 'application/json',
|
||||
cache:false,
|
||||
timeout : 5000, //超时时间设置,单位毫秒
|
||||
converters:{
|
||||
"text json": JSONbig.parse
|
||||
}
|
||||
});
|
||||
|
||||
$httpProvider.defaults.cache = false;
|
||||
|
||||
$routeProvider.when('/', {
|
||||
templateUrl: 'view/pages/index.html',
|
||||
controller:'dashboardCtrl'
|
||||
}).when('/cluster', {
|
||||
templateUrl: 'view/pages/cluster.html',
|
||||
controller:'clusterController'
|
||||
}).when('/topic', {
|
||||
templateUrl: 'view/pages/topic.html',
|
||||
controller:'topicController'
|
||||
}).when('/consumer', {
|
||||
templateUrl: 'view/pages/consumer.html',
|
||||
controller:'consumerController'
|
||||
}).when('/producer', {
|
||||
templateUrl: 'view/pages/producer.html',
|
||||
controller:'producerController'
|
||||
}).when('/message', {
|
||||
templateUrl: 'view/pages/message.html',
|
||||
controller:'messageController'
|
||||
}).when('/ops', {
|
||||
templateUrl: 'view/pages/ops.html',
|
||||
controller:'opsController'
|
||||
}).when('/404', {
|
||||
templateUrl: '404'
|
||||
}).otherwise('404');
|
||||
|
||||
$translateProvider.translations('en',en);
|
||||
$translateProvider.translations('zh',zh);
|
||||
$translateProvider.preferredLanguage('en');
|
||||
$translateProvider.useCookieStorage();
|
||||
// $translateProvider.useSanitizeValueStrategy('sanitize');
|
||||
|
||||
}]);
|
||||
|
||||
app.filter('range', function() {
|
||||
return function(input, range) {
|
||||
var total = parseInt(range.totalPage) + 1;
|
||||
var count = 5;
|
||||
for (var i = range.start; i<total; i++) {
|
||||
if(count > 0){
|
||||
input.push(i);
|
||||
count -- ;
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return input;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
app.filter('dict',['getDictName',function(getDictName){
|
||||
return function(value,type){
|
||||
return getDictName(type,value);
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* 数组扩展方法,移除数组中某一元素或某一段元素
|
||||
* @param from 需要移除元素的索引开始值(只传一个参数表示单独移除该元素)
|
||||
* @param to 需要移除元素的索引结束值
|
||||
* @returns {*}
|
||||
*/
|
||||
Array.prototype.remove = function(from, to) {
|
||||
var rest = this.slice((to || from) + 1 || this.length);
|
||||
this.length = from < 0 ? this.length + from : from;
|
||||
return this.push.apply(this, rest);
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据元素值查询数组中元素的索引
|
||||
* @param val
|
||||
* @returns {number}
|
||||
*/
|
||||
Array.prototype.indexOf = function(val) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (this[i] == val) return i;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
72
src/main/resources/static/src/cluster.js
Normal file
72
src/main/resources/static/src/cluster.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
app.controller('clusterController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) {
|
||||
$scope.clusterMap = {};//cluster:brokerNameList
|
||||
$scope.brokerMap = {};//brokerName:{id:addr}
|
||||
$scope.brokerDetail = {};//{brokerName,id:detail}
|
||||
$scope.clusterNames = [];
|
||||
$scope.selectedCluster = "";
|
||||
var callback = function (resp) {
|
||||
if (resp.status == 0) {
|
||||
$scope.clusterMap = resp.data.clusterInfo.clusterAddrTable;
|
||||
$scope.brokerMap = resp.data.clusterInfo.brokerAddrTable;
|
||||
$scope.brokerDetail = resp.data.brokerServer;
|
||||
$.each($scope.clusterMap,function(clusterName,clusterBrokersNames){
|
||||
$scope.clusterNames.push(clusterName);
|
||||
});
|
||||
if ($scope.clusterNames.length > 0) {
|
||||
$scope.selectedCluster = $scope.clusterNames[0];
|
||||
}
|
||||
$scope.brokers = tools.generateBrokerMap($scope.brokerDetail,$scope.clusterMap,$scope.brokerMap);
|
||||
$scope.switchCluster();
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
}
|
||||
|
||||
remoteApi.queryClusterList(callback);
|
||||
|
||||
$scope.switchCluster = function(){
|
||||
$scope.instances = $scope.brokers[$scope.selectedCluster];
|
||||
}
|
||||
|
||||
$scope.showDetail = function (brokerName,index) {
|
||||
$scope.detail = $scope.brokerDetail[brokerName][index];
|
||||
$scope.brokerName = brokerName;
|
||||
$scope.index = index;
|
||||
$(".brokerModal").modal();
|
||||
}
|
||||
|
||||
$scope.showConfig = function (brokerAddr,brokerName,index) {
|
||||
$scope.brokerAddr = brokerAddr;
|
||||
$scope.brokerName = brokerName;
|
||||
$scope.index = index;
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "cluster/brokerConfig.query",
|
||||
params:{brokerAddr:brokerAddr}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
$scope.brokerConfig = resp.data;
|
||||
$(".configModal").modal();
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
}
|
||||
}])
|
||||
350
src/main/resources/static/src/consumer.js
Normal file
350
src/main/resources/static/src/consumer.js
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var module = app;
|
||||
|
||||
module.controller('consumerController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
itemsPerPage: 10,
|
||||
pagesLength: 15,
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.showConsumerGroupList(this.currentPage,this.totalItems);
|
||||
}
|
||||
};
|
||||
$scope.sortKey = null;
|
||||
$scope.sortOrder=1;
|
||||
$scope.intervalProcessSwitch = false;
|
||||
$scope.intervalProcess = null;
|
||||
$scope.allConsumerGrouopList = [];
|
||||
$scope.consumerGroupShowList = [];
|
||||
$scope.sortByKey = function (key) {
|
||||
$scope.paginationConf.currentPage=1;
|
||||
$scope.sortOrder = -$scope.sortOrder;
|
||||
$scope.sortKey = key;
|
||||
$scope.doSort();
|
||||
};
|
||||
|
||||
$scope.doSort = function (){// todo how to change this fe's code ? (it's dirty)
|
||||
if($scope.sortKey == 'diffTotal'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.diffTotal > b.diffTotal) ? $scope.sortOrder : ((b.diffTotal > a.diffTotal) ? -$scope.sortOrder : 0);} );
|
||||
}
|
||||
if($scope.sortKey == 'group'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.group > b.group) ? $scope.sortOrder : ((b.group > a.group) ? -$scope.sortOrder : 0);} );
|
||||
}
|
||||
if($scope.sortKey == 'count'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.count > b.count) ? $scope.sortOrder : ((b.count > a.count) ? -$scope.sortOrder : 0);} );
|
||||
}
|
||||
if($scope.sortKey == 'consumeTps'){
|
||||
$scope.allConsumerGrouopList.sort(function(a,b) {return (a.consumeTps > b.consumeTps) ? $scope.sortOrder : ((b.consumeTps > a.consumeTps) ? -$scope.sortOrder : 0);} );
|
||||
}
|
||||
$scope.filterList($scope.paginationConf.currentPage)
|
||||
};
|
||||
$scope.refreshConsumerData = function () {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/groupList.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
$scope.allConsumerGrouopList = resp.data;
|
||||
console.log($scope.allConsumerGrouopList);
|
||||
console.log(JSON.stringify(resp));
|
||||
$scope.showConsumerGroupList($scope.paginationConf.currentPage,$scope.allConsumerGrouopList.length);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.monitor = function(consumerGroupName){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "monitor/consumerMonitorConfigByGroupName.query",
|
||||
params:{consumeGroupName:consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
// if(resp.status ==0){
|
||||
ngDialog.open({
|
||||
template: 'consumerMonitorDialog',
|
||||
controller: 'consumerMonitorDialogController',
|
||||
data:{consumerGroupName:consumerGroupName,data:resp.data}
|
||||
});
|
||||
// }else {
|
||||
// Notification.error({message: resp.errMsg, delay: 2000});
|
||||
// }
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.$watch('intervalProcessSwitch', function () {
|
||||
if ($scope.intervalProcess != null) {
|
||||
clearInterval($scope.intervalProcess);
|
||||
$scope.intervalProcess = null;
|
||||
}
|
||||
if ($scope.intervalProcessSwitch) {
|
||||
$scope.intervalProcess = setInterval($scope.refreshConsumerData, 10000);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$scope.refreshConsumerData();
|
||||
$scope.filterStr="";
|
||||
$scope.$watch('filterStr', function() {
|
||||
$scope.paginationConf.currentPage=1;
|
||||
$scope.filterList(1)
|
||||
});
|
||||
|
||||
$scope.filterList = function (currentPage) {
|
||||
var lowExceptStr = $scope.filterStr.toLowerCase();
|
||||
var canShowList = [];
|
||||
$scope.allConsumerGrouopList.forEach(function(element) {
|
||||
console.log(element)
|
||||
if (element.group.toLowerCase().indexOf(lowExceptStr) != -1){
|
||||
canShowList.push(element);
|
||||
}
|
||||
});
|
||||
$scope.paginationConf.totalItems =canShowList.length;
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>canShowList.length?canShowList.length:from + perPage;
|
||||
$scope.consumerGroupShowList = canShowList.slice(from, to);
|
||||
};
|
||||
|
||||
|
||||
$scope.showConsumerGroupList = function (currentPage,totalItem) {
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>totalItem?totalItem:from + perPage;
|
||||
$scope.consumerGroupShowList = $scope.allConsumerGrouopList.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem ;
|
||||
console.log($scope.consumerGroupShowList)
|
||||
console.log($scope.paginationConf.totalItems)
|
||||
$scope.doSort()
|
||||
};
|
||||
$scope.openAddDialog = function () {
|
||||
$scope.openCreateOrUpdateDialog(null);
|
||||
};
|
||||
$scope.openCreateOrUpdateDialog = function(request){
|
||||
var bIsUpdate = true;
|
||||
if(request == null){
|
||||
request = [{
|
||||
brokerNameList: [],
|
||||
subscriptionGroupConfig: {
|
||||
groupName: "",
|
||||
consumeEnable: true,
|
||||
consumeFromMinEnable: true,
|
||||
consumeBroadcastEnable: true,
|
||||
retryQueueNums: 1,
|
||||
retryMaxTimes: 16,
|
||||
brokerId: 0,
|
||||
whichBrokerWhenConsumeSlowly: 1
|
||||
}
|
||||
}];
|
||||
bIsUpdate = false;
|
||||
}
|
||||
console.log(request);
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "cluster/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'consumerModifyDialog',
|
||||
controller: 'consumerModifyDialogController',
|
||||
data:{
|
||||
consumerRequestList:request,
|
||||
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList:Object.keys(resp.data.brokerServer),
|
||||
bIsUpdate:bIsUpdate
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.detail = function(consumerGroupName){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/queryTopicByConsumer.query",
|
||||
params:{consumerGroup:consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'consumerTopicViewDialog',
|
||||
controller: 'consumerTopicViewDialogController',
|
||||
data:{consumerGroupName:consumerGroupName,data:resp.data}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.client = function(consumerGroupName){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/consumerConnection.query",
|
||||
params:{consumerGroup:consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'clientInfoDialog',
|
||||
// controller: 'addTopicDialogController',
|
||||
data:{data:resp.data,consumerGroupName:consumerGroupName}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.updateConfigDialog = function(consumerGroupName){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/examineSubscriptionGroupConfig.query",
|
||||
params:{consumerGroup:consumerGroupName}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
console.log(resp);
|
||||
$scope.openCreateOrUpdateDialog(resp.data);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
$scope.delete = function(consumerGroupName){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/fetchBrokerNameList.query",
|
||||
params:{
|
||||
consumerGroup:consumerGroupName
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
console.log(resp);
|
||||
|
||||
ngDialog.open({
|
||||
template: 'deleteConsumerDialog',
|
||||
controller: 'deleteConsumerDialogController',
|
||||
data:{
|
||||
// allClusterList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList:resp.data,
|
||||
consumerGroupName:consumerGroupName
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}])
|
||||
module.controller('consumerMonitorDialogController', function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.createOrUpdateConsumerMonitor = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "monitor/createOrUpdateConsumerMonitor.do",
|
||||
params:{consumeGroupName:$scope.ngDialogData.consumerGroupName,
|
||||
minCount:$scope.ngDialogData.data.minCount,
|
||||
maxDiffTotal:$scope.ngDialogData.data.maxDiffTotal}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
Notification.info({message: "delete success!", delay: 2000});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
module.controller('deleteConsumerDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.selectedClusterList = [];
|
||||
$scope.selectedBrokerNameList = [];
|
||||
$scope.delete = function () {
|
||||
console.log($scope.selectedClusterList);
|
||||
console.log($scope.selectedBrokerNameList);
|
||||
console.log($scope.ngDialogData.consumerGroupName);
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "consumer/deleteSubGroup.do",
|
||||
data:{groupName:$scope.ngDialogData.consumerGroupName,
|
||||
brokerNameList:$scope.selectedBrokerNameList}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
Notification.info({message: "delete success!", delay: 2000});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('consumerModifyDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.postConsumerRequest = function (consumerRequest) {
|
||||
var request = JSON.parse(JSON.stringify(consumerRequest));
|
||||
console.log(request);
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "consumer/createOrUpdate.do",
|
||||
data:request
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
Notification.info({message: "update success!", delay: 2000});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('consumerTopicViewDialogController', ['$scope', 'ngDialog', '$http', 'Notification', function ($scope, ngDialog, $http, Notification) {
|
||||
$scope.consumerRunningInfo = function (consumerGroup, clientId, jstack) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "consumer/consumerRunningInfo.query",
|
||||
params: {
|
||||
consumerGroup: consumerGroup,
|
||||
clientId: clientId,
|
||||
jstack: jstack
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'consumerClientDialog',
|
||||
data:{consumerClientInfo:resp.data,
|
||||
clientId:clientId}
|
||||
});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
}]
|
||||
);
|
||||
|
||||
|
||||
|
||||
557
src/main/resources/static/src/controller.js
Normal file
557
src/main/resources/static/src/controller.js
Normal file
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
app.controller('AppCtrl', ['$scope','$rootScope','$cookies','$location','$translate', function ($scope,$rootScope,$cookies,$location,$translate) {
|
||||
$scope.changeTranslate = function(langKey){
|
||||
$translate.use(langKey);
|
||||
}
|
||||
}]);
|
||||
|
||||
app.controller('dashboardCtrl', ['$scope','$rootScope','$translate','$filter','Notification','remoteApi','tools', function ($scope,$rootScope,$translate,$filter,Notification,remoteApi,tools) {
|
||||
|
||||
$scope.barChart = echarts.init(document.getElementById('main'));
|
||||
$scope.lineChart = echarts.init(document.getElementById('line'));
|
||||
$scope.topicBarChart = echarts.init(document.getElementById('topicBar'));
|
||||
$scope.topicLineChart = echarts.init(document.getElementById('topicLine'));
|
||||
$scope.timepickerOptions ={format: 'YYYY-MM-DD', showClear: true};
|
||||
$scope.topicNames = [];
|
||||
|
||||
$translate('BROKER').then(function (broker) {
|
||||
$scope.BROKER_TITLE = broker;
|
||||
initBrokerBarChart();
|
||||
initBrokerLineChart();
|
||||
}, function (translationId) {
|
||||
$scope.BROKER_TITLE = translationId;
|
||||
});
|
||||
|
||||
$translate('TOPIC').then(function (topic) {
|
||||
$scope.TOPIC_TITLE = topic;
|
||||
initTopicBarChart();
|
||||
initTopicLineChart();
|
||||
}, function (translationId) {
|
||||
$scope.TOPIC_TITLE = translationId;
|
||||
});
|
||||
|
||||
var initBrokerBarChart = function(){
|
||||
$scope.barChart.setOption({
|
||||
title: {
|
||||
text:$scope.BROKER_TITLE + ' TOP 10'
|
||||
},
|
||||
tooltip: {},
|
||||
legend: {
|
||||
data:['TotalMsg']
|
||||
},
|
||||
axisPointer : {
|
||||
type : 'shadow'
|
||||
},
|
||||
xAxis: {
|
||||
data: [],
|
||||
axisLabel: {
|
||||
inside: false,
|
||||
textStyle: {
|
||||
color: '#000000'
|
||||
},
|
||||
rotate: 0,
|
||||
interval:0
|
||||
},
|
||||
axisTick: {
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
z: 10
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
axisLabel: {
|
||||
formatter: function(value){
|
||||
return value.toFixed(2);
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: 'TotalMsg',
|
||||
type: 'bar',
|
||||
data: []
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
var initBrokerLineChart = function(){
|
||||
$scope.lineChart.setOption({
|
||||
title: {
|
||||
text: $scope.BROKER_TITLE + ' 5min trend'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
animation: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '80%'],
|
||||
axisLabel: {
|
||||
formatter: function(value){
|
||||
return value.toFixed(2);
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
dataZoom: [{
|
||||
type: 'inside',
|
||||
start: 90,
|
||||
end: 100
|
||||
}, {
|
||||
start: 0,
|
||||
end: 10,
|
||||
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
||||
handleSize: '80%',
|
||||
handleStyle: {
|
||||
color: '#fff',
|
||||
shadowBlur: 3,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.6)',
|
||||
shadowOffsetX: 2,
|
||||
shadowOffsetY: 2
|
||||
}
|
||||
}],
|
||||
legend: {
|
||||
data: [],
|
||||
top:30
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false,
|
||||
data: []
|
||||
},
|
||||
series: []
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
var initTopicBarChart = function(){
|
||||
$scope.topicBarChart.setOption({
|
||||
title: {
|
||||
text:$scope.TOPIC_TITLE + ' TOP 10'
|
||||
},
|
||||
tooltip: {},
|
||||
legend: {
|
||||
data:['TotalMsg']
|
||||
},
|
||||
axisPointer : {
|
||||
type : 'shadow'
|
||||
},
|
||||
xAxis: {
|
||||
data: [],
|
||||
axisLabel: {
|
||||
inside: false,
|
||||
textStyle: {
|
||||
color: '#000000'
|
||||
},
|
||||
rotate: 0,
|
||||
interval:0
|
||||
},
|
||||
axisTick: {
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
z: 10
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
axisLabel: {
|
||||
formatter: function(value){
|
||||
return value.toFixed(2);
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: 'TotalMsg',
|
||||
type: 'bar',
|
||||
data: []
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
var initTopicLineChart = function(){
|
||||
var _option = {
|
||||
baseOption:{
|
||||
title: {
|
||||
text: $scope.TOPIC_TITLE + ' 5min trend'
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: 'none'
|
||||
},
|
||||
restore: {},
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
grid:{
|
||||
top:100
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
animation: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '80%'],
|
||||
axisLabel: {
|
||||
formatter: function(value){
|
||||
return value.toFixed(2);
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
dataZoom: [{
|
||||
type: 'inside',
|
||||
start: 90,
|
||||
end: 100
|
||||
}, {
|
||||
start: 0,
|
||||
end: 10,
|
||||
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
|
||||
handleSize: '80%',
|
||||
handleStyle: {
|
||||
color: '#fff',
|
||||
shadowBlur: 3,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.6)',
|
||||
shadowOffsetX: 2,
|
||||
shadowOffsetY: 2
|
||||
}
|
||||
}],
|
||||
legend:{
|
||||
data:[],
|
||||
top:30
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
boundaryGap: false,
|
||||
data: []
|
||||
},
|
||||
series: []
|
||||
}
|
||||
}
|
||||
$scope.topicLineChart.setOption(_option)
|
||||
|
||||
}
|
||||
|
||||
var getBrokerBarChartOp = function(xAxisData,data){
|
||||
// 指定图表的配置项和数据
|
||||
var option = {
|
||||
xAxis: {
|
||||
data: xAxisData,
|
||||
axisLabel: {
|
||||
inside: false,
|
||||
textStyle: {
|
||||
color: '#000000'
|
||||
},
|
||||
rotate: 0,
|
||||
interval:0
|
||||
},
|
||||
axisTick: {
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
z: 10
|
||||
},
|
||||
series: [{
|
||||
name: 'TotalMsg',
|
||||
type: 'bar',
|
||||
data: data
|
||||
}]
|
||||
};
|
||||
|
||||
$scope.barChart.setOption(option);
|
||||
}
|
||||
|
||||
var callback = function (resp) {
|
||||
$scope.barChart.hideLoading();
|
||||
if (resp.status == 0) {
|
||||
var clusterMap = resp.data.clusterInfo.clusterAddrTable;
|
||||
var brokerMap = resp.data.clusterInfo.brokerAddrTable;
|
||||
var brokerDetail = resp.data.brokerServer;
|
||||
var clusterMap = tools.generateBrokerMap(brokerDetail,clusterMap,brokerMap);
|
||||
$scope.brokerArray = [];
|
||||
$.each(clusterMap,function(clusterName,brokers){
|
||||
$.each(brokers,function(i,broker){
|
||||
$scope.brokerArray.push(broker)
|
||||
})
|
||||
})
|
||||
|
||||
//sort the brokerArray
|
||||
$scope.brokerArray.sort(function(firstBroker,lastBroker){
|
||||
var firstTotalMsg = parseFloat(firstBroker.msgGetTotalTodayNow);
|
||||
var lastTotalMsg = parseFloat(lastBroker.msgGetTotalTodayNow);
|
||||
return lastTotalMsg-firstTotalMsg;
|
||||
});
|
||||
|
||||
var xAxisData = [],
|
||||
data = [];
|
||||
|
||||
$.each($scope.brokerArray,function(i,broker){
|
||||
if(i > 9){
|
||||
return false;
|
||||
}
|
||||
xAxisData.push(broker.brokerName + ":" + broker.index);
|
||||
data.push(broker.msgGetTotalTodayNow);
|
||||
})
|
||||
getBrokerBarChartOp(xAxisData,data);
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
}
|
||||
|
||||
$scope.barChart.showLoading();
|
||||
remoteApi.queryClusterList(callback);
|
||||
|
||||
$scope.topicBarChart.showLoading();
|
||||
remoteApi.queryTopicCurrentData(function(resp){
|
||||
$scope.topicBarChart.hideLoading();
|
||||
if (resp.status == 0) {
|
||||
var topicList = resp.data;
|
||||
topicList.sort(function(first,last){
|
||||
var firstTotalMsg = parseFloat(first.split(",")[1]);
|
||||
var lastTotalMsg = parseFloat(last.split(",")[1]);
|
||||
return lastTotalMsg-firstTotalMsg;
|
||||
})
|
||||
|
||||
var xAxisData = [];
|
||||
var data = [];
|
||||
$.each(topicList,function (i,currentData) {
|
||||
var currentArray = currentData.split(",");
|
||||
$scope.topicNames.push(currentArray[0]);
|
||||
if(!angular.isDefined($scope.selectedTopic)){
|
||||
$scope.selectedTopic = currentArray[0];
|
||||
}
|
||||
})
|
||||
$.each(topicList,function (i, currentData) {
|
||||
if(i > 9){
|
||||
return false;
|
||||
}
|
||||
var currentArray = currentData.split(",");
|
||||
xAxisData.push(currentArray[0]);
|
||||
data.push(currentArray[1]);
|
||||
})
|
||||
// 指定图表的配置项和数据
|
||||
var option = {
|
||||
xAxis: {
|
||||
data: xAxisData,
|
||||
axisLabel: {
|
||||
inside: false,
|
||||
textStyle: {
|
||||
color: '#000000'
|
||||
},
|
||||
rotate: 60,
|
||||
interval:0
|
||||
},
|
||||
axisTick: {
|
||||
show: true
|
||||
},
|
||||
axisLine: {
|
||||
show: true
|
||||
},
|
||||
z: 10
|
||||
},
|
||||
series: [{
|
||||
name: 'TotalMsg',
|
||||
type: 'bar',
|
||||
data: data
|
||||
}]
|
||||
};
|
||||
$scope.topicBarChart.setOption(option);
|
||||
queryLineData();
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
var getBrokerLineChart = function(legend,data){
|
||||
var series = [];
|
||||
var xAxisData = [];
|
||||
var flag = true;
|
||||
var i = 0;
|
||||
$.each(data,function(key,value){
|
||||
// if(i > 9 ){
|
||||
// return false;
|
||||
// }
|
||||
var _tps = [];
|
||||
$.each(value,function(i,tpsValue){
|
||||
var tpsArray = tpsValue.split(",");
|
||||
if(flag){
|
||||
xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss"));
|
||||
}
|
||||
_tps.push(tpsArray[1]);
|
||||
})
|
||||
flag = false;
|
||||
var _series = {
|
||||
name:key,
|
||||
type:'line',
|
||||
smooth:true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
data: _tps
|
||||
}
|
||||
series.push(_series);
|
||||
i++
|
||||
})
|
||||
|
||||
var option = {
|
||||
legend: {
|
||||
data: legend
|
||||
},
|
||||
color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", '#EE7942'],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: xAxisData
|
||||
},
|
||||
series: series
|
||||
};
|
||||
return option;
|
||||
}
|
||||
|
||||
var getTopicLineChart = function(legend,data){
|
||||
var series = [];
|
||||
var xAxisData = [];
|
||||
var flag = true;
|
||||
var i = 0;
|
||||
$.each(data,function(key,value){
|
||||
var _tps = [];
|
||||
$.each(value,function(i,tpsValue){
|
||||
var tpsArray = tpsValue.split(",");
|
||||
if(flag){
|
||||
xAxisData.push($filter('date')(tpsArray[0], "HH:mm:ss"));
|
||||
}
|
||||
_tps.push(tpsArray[3]);
|
||||
})
|
||||
flag = false;
|
||||
var _series = {
|
||||
name:key,
|
||||
type:'line',
|
||||
smooth:true,
|
||||
symbol: 'none',
|
||||
sampling: 'average',
|
||||
data: _tps
|
||||
}
|
||||
series.push(_series);
|
||||
i++
|
||||
})
|
||||
|
||||
var option = {
|
||||
baseOption:{
|
||||
legend: {
|
||||
data: legend
|
||||
},
|
||||
// color: ["#FF0000", "#00BFFF", "#FF00FF", "#1ce322", "#000000", '#EE7942'],
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: xAxisData
|
||||
},
|
||||
series: series
|
||||
},
|
||||
media:[
|
||||
{
|
||||
query:{},
|
||||
option:{
|
||||
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
};
|
||||
return option;
|
||||
}
|
||||
|
||||
|
||||
var queryLineData = function () {
|
||||
var _date;
|
||||
if($scope.date != null){
|
||||
_date = $filter('date')($scope.date.valueOf(), "yyyy-MM-dd");
|
||||
}else{
|
||||
_date = $filter('date')(new Date(), "yyyy-MM-dd");
|
||||
}
|
||||
// $scope.lineChart.showLoading();
|
||||
remoteApi.queryBrokerHisData(_date,function(resp){
|
||||
// $scope.lineChart.hideLoading();
|
||||
if (resp.status == 0) {
|
||||
var _data = {}
|
||||
var _xAxisData = [];
|
||||
$.each(resp.data,function(address,values){
|
||||
_data[address] = values;
|
||||
_xAxisData.push(address);
|
||||
})
|
||||
$scope.lineChart.setOption(getBrokerLineChart(_xAxisData,_data));
|
||||
}else{
|
||||
Notification.error({message: "" + resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
|
||||
$scope.topicLineChart.showLoading();
|
||||
remoteApi.queryTopicHisData(_date,$scope.selectedTopic,function (resp) {
|
||||
$scope.topicLineChart.hideLoading();
|
||||
if (resp.status == 0) {
|
||||
var _data = {};
|
||||
_data[$scope.selectedTopic] = resp.data;
|
||||
var _xAxisData = $scope.selectedTopic;
|
||||
$scope.topicLineChart.setOption(getTopicLineChart(_xAxisData,_data));
|
||||
}else{
|
||||
Notification.error({message: "" + resp.errMsg, delay: 2000});
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
//router after will clear this thread
|
||||
$rootScope._thread = setInterval( queryLineData, tools.dashboardRefreshTime);
|
||||
|
||||
|
||||
}]);
|
||||
|
||||
|
||||
4
src/main/resources/static/src/data/dict.json
Normal file
4
src/main/resources/static/src/data/dict.json
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
{"TYPE":"DEMO_TYPE","DICT_VALUE":"0","DICT_NAME":"test1"},
|
||||
{"TYPE":"DEMO_TYPE","DICT_VALUE":"1","DICT_NAME":"test2"}
|
||||
]
|
||||
80
src/main/resources/static/src/i18n/en.js
Normal file
80
src/main/resources/static/src/i18n/en.js
Normal file
@@ -0,0 +1,80 @@
|
||||
var en = {
|
||||
"TITLE": "RocketMq-Console-Ng",
|
||||
"CLOSE": "Close",
|
||||
"NO": "NO.",
|
||||
"ADDRESS": "Address",
|
||||
"VERSION": "Version",
|
||||
"PRO_MSG_TPS": "Produce Massage TPS",
|
||||
"CUS_MSG_TPS": "Consumer Massage TPS",
|
||||
"YESTERDAY_PRO_COUNT": "Yesterday Produce Count",
|
||||
"YESTERDAY_CUS_COUNT": "Yesterday Consume Count",
|
||||
"TODAY_PRO_COUNT": "Today Produce Count",
|
||||
"TODAY_CUS_COUNT": "Today Ponsume Count",
|
||||
"INSTANCE": "Instance",
|
||||
"SPLIT": "Broker",
|
||||
"CLUSTER": "Cluster",
|
||||
"CLUSTER_DETAIL": "Cluster Detail",
|
||||
"TOPIC": "Topic",
|
||||
"SUBSCRIPTION_GROUP":"SubscriptionGroup",
|
||||
"PRODUCER_GROUP":"ProducerGroup",
|
||||
"CONSUMER":"Consumer",
|
||||
"PRODUCER":"Producer",
|
||||
"MESSAGE":"Message",
|
||||
"COMMIT": "Commit",
|
||||
"OPERATION": "Operation",
|
||||
"ADD": "Add",
|
||||
"UPDATE": "Update",
|
||||
"STATUS": "Status",
|
||||
"ROUTER": "Router",
|
||||
"MANAGE": "Manage",
|
||||
"CONFIG": "Config",
|
||||
"SEND_MSG": "Send Massage",
|
||||
"RESET_CUS_OFFSET": "Reset Consumer Offset",
|
||||
"DELETE": "Delete",
|
||||
"CHANGE_LANG": "ChangeLanguage",
|
||||
"BROKER": "Broker",
|
||||
"NORMAL": "NORMAL",
|
||||
"RETRY": "RETRY",
|
||||
"DLQ": "DLQ",
|
||||
"QUANTITY":"Quantity",
|
||||
"TYPE":"Type",
|
||||
"MODE":"Mode",
|
||||
"DELAY":"Delay",
|
||||
"DASHBOARD":"Dashboard",
|
||||
"CONSUME_DETAIL":"CONSUME DETAIL",
|
||||
"CLIENT":"CLIENT",
|
||||
"LAST_CONSUME_TIME":"LastConsumeTime",
|
||||
"TIME":"Time",
|
||||
"RESET":"RESET",
|
||||
"DATE":"Date",
|
||||
"NO_DATA":"NO DATA",
|
||||
"SEARCH":"Search",
|
||||
"BEGIN":"Begin",
|
||||
"END":"End",
|
||||
"TOPIC_CHANGE":"Topic Change",
|
||||
"SEND":"Send",
|
||||
"SUBSCRIPTION_CHANGE":"Subscription Change",
|
||||
"QUEUE":"Queue",
|
||||
"MIN_OFFSET":"minOffset",
|
||||
"MAX_OFFSET":"maxOffset",
|
||||
"LAST_UPDATE_TIME_STAMP":"lastUpdateTimeStamp",
|
||||
"QUEUE_DATAS":"queueDatas",
|
||||
"READ_QUEUE_NUMS":"readQueueNums",
|
||||
"WRITE_QUEUE_NUMS":"writeQueueNums",
|
||||
"PERM":"perm",
|
||||
"TAG":"Tag",
|
||||
"KEY":"Key",
|
||||
"MESSAGE_BODY":"Message Body",
|
||||
"TOPIC_NAME":"topicName",
|
||||
"ORDER":"order",
|
||||
"CONSUMER_CLIENT":"consumerClient",
|
||||
"BROKER_OFFSET":"brokerOffset",
|
||||
"CONSUMER_OFFSET":"consumerOffset",
|
||||
"DIFF_TOTAL":"diffTotal",
|
||||
"LAST_TIME_STAMP":"lastTimeStamp",
|
||||
"RESET_OFFSET":"resetOffset",
|
||||
"CLUSTER_NAME":"clusterName",
|
||||
"OPS":"OPS",
|
||||
"AUTO_REFRESH":"AUTO_REFRESH",
|
||||
"REFRESH":"REFRESH"
|
||||
}
|
||||
80
src/main/resources/static/src/i18n/zh.js
Normal file
80
src/main/resources/static/src/i18n/zh.js
Normal file
@@ -0,0 +1,80 @@
|
||||
var zh = {
|
||||
"TITLE": "RocketMq控制台",
|
||||
"CLOSE": "关闭",
|
||||
"NO": "编号",
|
||||
"ADDRESS": "地址",
|
||||
"VERSION": "版本",
|
||||
"PRO_MSG_TPS": "生产消息TPS",
|
||||
"CUS_MSG_TPS": "消费消息TPS",
|
||||
"YESTERDAY_PRO_COUNT": "昨日生产总数",
|
||||
"YESTERDAY_CUS_COUNT": "昨日消费总数",
|
||||
"TODAY_PRO_COUNT": "今天生产总数",
|
||||
"TODAY_CUS_COUNT": "今天消费总数",
|
||||
"INSTANCE": "实例",
|
||||
"SPLIT": "分片",
|
||||
"CLUSTER": "集群",
|
||||
"CLUSTER_DETAIL": "集群详情",
|
||||
"COMMIT": "提交",
|
||||
"TOPIC": "主题",
|
||||
"SUBSCRIPTION_GROUP":"订阅组",
|
||||
"PRODUCER_GROUP":"生产组",
|
||||
"CONSUMER":"消费者",
|
||||
"PRODUCER":"生产者",
|
||||
"MESSAGE":"消息",
|
||||
"OPERATION": "操作",
|
||||
"ADD": "新增",
|
||||
"UPDATE": "更新",
|
||||
"STATUS": "状态",
|
||||
"ROUTER": "路由",
|
||||
"MANAGE": "管理",
|
||||
"CONFIG": "配置",
|
||||
"SEND_MSG": "发送消息",
|
||||
"RESET_CUS_OFFSET": "重置消费位点",
|
||||
"DELETE": "删除",
|
||||
"CHANGE_LANG": "更换语言",
|
||||
"BROKER": "Broker",
|
||||
"NORMAL": "普通",
|
||||
"RETRY": "重试",
|
||||
"DLQ": "死信",
|
||||
"QUANTITY":"数量",
|
||||
"TYPE":"类型",
|
||||
"MODE":"模式",
|
||||
"DELAY":"延迟",
|
||||
"DASHBOARD":"驾驶舱",
|
||||
"CONSUME_DETAIL":"消费详情",
|
||||
"CLIENT":"终端",
|
||||
"LAST_CONSUME_TIME":"最后消费时间",
|
||||
"TIME":"时间点",
|
||||
"RESET":"重置",
|
||||
"DATE":"日期",
|
||||
"NO_DATA":"暂无数据",
|
||||
"SEARCH":"搜索",
|
||||
"BEGIN":"开始",
|
||||
"END":"结束",
|
||||
"TOPIC_CHANGE":"修改主题",
|
||||
"SEND":"发送",
|
||||
"SUBSCRIPTION_CHANGE":"修改订阅",
|
||||
"QUEUE":"队列",
|
||||
"MIN_OFFSET":"最小位点",
|
||||
"MAX_OFFSET":"最大位点",
|
||||
"LAST_UPDATE_TIME_STAMP":"上次更新时间",
|
||||
"QUEUE_DATAS":"队列信息",
|
||||
"READ_QUEUE_NUMS":"读队列数量",
|
||||
"WRITE_QUEUE_NUMS":"写队列数量",
|
||||
"PERM":"perm",
|
||||
"TAG":"标签",
|
||||
"KEY":"值",
|
||||
"MESSAGE_BODY":"消息主体",
|
||||
"TOPIC_NAME":"主题名",
|
||||
"ORDER":"顺序",
|
||||
"CONSUMER_CLIENT":"消费者终端",
|
||||
"BROKER_OFFSET":"代理者位点",
|
||||
"CONSUMER_OFFSET":"消费者位点",
|
||||
"DIFF_TOTAL":"差值",
|
||||
"LAST_TIME_STAMP":"上次时间",
|
||||
"RESET_OFFSET":"重置位点",
|
||||
"CLUSTER_NAME":"集群名",
|
||||
"OPS":"运维",
|
||||
"AUTO_REFRESH":"自动刷新",
|
||||
"REFRESH":"刷新"
|
||||
}
|
||||
200
src/main/resources/static/src/message.js
Normal file
200
src/main/resources/static/src/message.js
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var module = app;
|
||||
|
||||
module.controller('messageController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.allTopicList = [];
|
||||
$scope.selectedTopic =[];
|
||||
$scope.key ="";
|
||||
$scope.messageId ="";
|
||||
$scope.queryMessageByTopicResult=[];
|
||||
$scope.queryMessageByTopicAndKeyResult=[];
|
||||
$scope.queryMessageByMessageIdResult={};
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
$scope.allTopicList = resp.data.topicList.sort();
|
||||
console.log($scope.allTopicList);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
$scope.timepickerBegin = moment().subtract(1, 'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerEnd = moment().add(1,'hour').format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepickerOptions ={format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
itemsPerPage: 20,
|
||||
pagesLength: 15,
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.changeShowMessageList(this.currentPage,this.totalItems);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.queryMessageByTopic = function () {
|
||||
console.log($scope.selectedTopic);
|
||||
console.log($scope.timepickerBegin)
|
||||
console.log($scope.timepickerEnd)
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "message/queryMessageByTopic.query",
|
||||
params: {
|
||||
topic: $scope.selectedTopic,
|
||||
begin: $scope.timepickerBegin.valueOf(),
|
||||
end: $scope.timepickerEnd.valueOf()
|
||||
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
$scope.queryMessageByTopicResult = resp.data;
|
||||
$scope.changeShowMessageList(1,$scope.queryMessageByTopicResult.length);
|
||||
// todo
|
||||
// console.log($scope.queryMessageByTopicResult);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryMessageByTopicAndKey = function () {
|
||||
console.log($scope.selectedTopic);
|
||||
console.log($scope.key);
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "message/queryMessageByTopicAndKey.query",
|
||||
params: {
|
||||
topic: $scope.selectedTopic,
|
||||
key:$scope.key
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
$scope.queryMessageByTopicAndKeyResult = resp.data;
|
||||
console.log($scope.queryMessageByTopicAndKeyResult);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryMessageByBrokerAndOffset = function (storeHost,commitLogOffset) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "message/viewMessageByBrokerAndOffset.query",
|
||||
params: {
|
||||
brokerHost: storeHost.address,
|
||||
port:storeHost.port,
|
||||
offset: commitLogOffset
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'messageDetailViewDialog',
|
||||
controller: 'messageDetailViewDialogController',
|
||||
data: resp.data
|
||||
});
|
||||
} else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryMessageByMessageId = function (messageId,topic) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "message/viewMessage.query",
|
||||
params: {
|
||||
msgId: messageId,
|
||||
topic:topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'messageDetailViewDialog',
|
||||
controller: 'messageDetailViewDialogController',
|
||||
data:resp.data
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.changeShowMessageList = function (currentPage,totalItem) {
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>totalItem?totalItem:from + perPage;
|
||||
$scope.messageShowList = $scope.queryMessageByTopicResult.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem ;
|
||||
};
|
||||
}]);
|
||||
|
||||
module.controller('messageDetailViewDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
|
||||
|
||||
$scope.resendMessage = function (msgId,topic,consumerGroup) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "message/consumeMessageDirectly.do",
|
||||
params: {
|
||||
msgId: msgId,
|
||||
consumerGroup:consumerGroup,
|
||||
topic:topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data:{
|
||||
result:resp.errMsg
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.showExceptionDesc = function (errmsg) {
|
||||
if(errmsg == null){
|
||||
errmsg = "Don't have Exception"
|
||||
}
|
||||
ngDialog.open({
|
||||
template: 'operationResultDialog',
|
||||
data:{
|
||||
result:errmsg
|
||||
}
|
||||
});
|
||||
};
|
||||
}]
|
||||
);
|
||||
59
src/main/resources/static/src/ops.js
Normal file
59
src/main/resources/static/src/ops.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
app.controller('opsController', ['$scope','$location','$http','Notification','remoteApi','tools', function ($scope,$location,$http,Notification,remoteApi,tools) {
|
||||
$scope.namesvrAddrList = [];
|
||||
$scope.useVIPChannel = true;
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "ops/homePage.query"
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
$scope.namesvrAddrList = resp.data.namesvrAddrList;
|
||||
$scope.useVIPChannel = resp.data.useVIPChannel
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.updateNameSvrAddr = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "ops/updateNameSvrAddr.do",
|
||||
params:{nameSvrAddrList:$scope.namesvrAddrList.join(";")}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "SUCCESS", delay: 2000});
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.updateIsVIPChannel = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "ops/updateIsVIPChannel.do",
|
||||
params:{useVIPChannel:$scope.useVIPChannel}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
Notification.info({message: "SUCCESS", delay: 2000});
|
||||
}else{
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]);
|
||||
49
src/main/resources/static/src/producer.js
Normal file
49
src/main/resources/static/src/producer.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
var module = app;
|
||||
module.controller('producerController', ['$scope', '$http','Notification',function ($scope, $http,Notification) {
|
||||
$scope.selectedTopic=[];
|
||||
$scope.producerGroup="";
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
$scope.allTopicList = resp.data.topicList.sort();
|
||||
console.log($scope.allTopicList);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
$scope.queryClientByTopicAndGroup = function () {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "producer/producerConnection.query",
|
||||
params:{
|
||||
topic:$scope.selectedTopic,
|
||||
producerGroup:$scope.producerGroup
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
$scope.connectionList = resp.data.connectionSet;
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
} ]);
|
||||
75
src/main/resources/static/src/remoteApi/remoteApi.js
Normal file
75
src/main/resources/static/src/remoteApi/remoteApi.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
app.service('remoteApi', ['$http','tools', function ($http,tools) {
|
||||
var queryTopic = function(callback){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/list.query"
|
||||
}).success(callback);
|
||||
}
|
||||
|
||||
var queryClusterList = function(callback){
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "cluster/list.query"
|
||||
}).success(callback);
|
||||
}
|
||||
|
||||
var queryBrokerHisData = function(date,callback){
|
||||
var url = 'dashboard/broker.query';
|
||||
var data = {date:date};
|
||||
var setting = {
|
||||
type: "GET",
|
||||
data:data,
|
||||
timeout:15000,//data is too large,so master set time out is long enough
|
||||
success:callback
|
||||
}
|
||||
$.ajax(url,setting)
|
||||
}
|
||||
|
||||
var queryTopicHisData = function(date,topicName,callback){
|
||||
var url = 'dashboard/topic.query';
|
||||
var data = {date:date,topicName:topicName};
|
||||
var setting = {
|
||||
type: "GET",
|
||||
data:data,
|
||||
timeout:15000,//data is too large,so master set time out is long enough
|
||||
success:callback
|
||||
}
|
||||
$.ajax(url,setting)
|
||||
}
|
||||
|
||||
var queryTopicCurrentData = function(callback){
|
||||
var url = 'dashboard/topicCurrent';
|
||||
var setting = {
|
||||
type: "GET",
|
||||
timeout:15000,//data is too large,so master set time out is long enough
|
||||
success:callback
|
||||
}
|
||||
$.ajax(url,setting)
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
queryTopic:queryTopic,
|
||||
queryClusterList:queryClusterList,
|
||||
queryBrokerHisData:queryBrokerHisData,
|
||||
queryTopicHisData:queryTopicHisData,
|
||||
queryTopicCurrentData:queryTopicCurrentData
|
||||
}
|
||||
}])
|
||||
|
||||
87
src/main/resources/static/src/tools/tools.js
Normal file
87
src/main/resources/static/src/tools/tools.js
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
app.service('tools', ['$http', function ($http) {
|
||||
|
||||
var ctx = "";
|
||||
var dashboardRefreshTime = 5000; // todo improve. when data size is large,request is too slow
|
||||
|
||||
var generateBrokerMap = function(brokerDetail,clusterMap,brokerMap){
|
||||
var map = {};
|
||||
$.each(brokerDetail,function(k,v){
|
||||
$.each(clusterMap,function (ck, cv) {
|
||||
if(angular.isUndefined(map[ck])){
|
||||
map[ck] = [];
|
||||
}
|
||||
$.each(cv,function(cvi,cvv){
|
||||
if(cvv == k){
|
||||
var index = 0;
|
||||
$.each(v,function(vi,vv){
|
||||
vv.split = k;
|
||||
vv.index = index;
|
||||
vv.address = brokerMap[cvv].brokerAddrs[index];
|
||||
vv.brokerName = brokerMap[cvv].brokerName;
|
||||
map[ck].push(vv);
|
||||
index++;
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
return map;
|
||||
}
|
||||
|
||||
var fastSort = function (arrayToSort, propertyToSortWith, sortDirection) {
|
||||
// temporary holder of position and sort-value
|
||||
var map = arrayToSort.map(function (e, i) {
|
||||
if (typeof e[propertyToSortWith] === 'string') {
|
||||
return { index: i, value: e[propertyToSortWith].toLowerCase() };
|
||||
}
|
||||
else {
|
||||
return { index: i, value: e[propertyToSortWith] };
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
// sorting the map containing the reduced values
|
||||
map.sort(function (a, b) {
|
||||
if (sortDirection === "ascending") {
|
||||
return +(a.value > b.value) || +(a.value === b.value) - 1;
|
||||
}
|
||||
else {
|
||||
return +(a.value < b.value) || +(a.value === b.value) - 1;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// container for the resulting order
|
||||
var result = map.map(function (e) {
|
||||
return arrayToSort[e.index];
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
return {
|
||||
generateBrokerMap:generateBrokerMap,
|
||||
fastSort:fastSort,
|
||||
ctx:ctx,
|
||||
dashboardRefreshTime:dashboardRefreshTime
|
||||
}
|
||||
}])
|
||||
|
||||
|
||||
|
||||
406
src/main/resources/static/src/topic.js
Normal file
406
src/main/resources/static/src/topic.js
Normal file
@@ -0,0 +1,406 @@
|
||||
/**
|
||||
* Created by tcrow on 2017/1/12 0012.
|
||||
*/
|
||||
var module = app;
|
||||
|
||||
module.directive('ngConfirmClick', [
|
||||
function(){
|
||||
return {
|
||||
link: function (scope, element, attr) {
|
||||
var msg = attr.ngConfirmClick || "Are you sure?";
|
||||
var clickAction = attr.confirmedClick;
|
||||
element.bind('click',function (event) {
|
||||
if ( window.confirm(msg) ) {
|
||||
scope.$eval(clickAction)
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
||||
module.controller('topicController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.paginationConf = {
|
||||
currentPage: 1,
|
||||
totalItems: 0,
|
||||
itemsPerPage: 10,
|
||||
pagesLength: 15,
|
||||
perPageOptions: [10],
|
||||
rememberPerPage: 'perPageItems',
|
||||
onChange: function () {
|
||||
$scope.showTopicList(this.currentPage,this.totalItems);
|
||||
|
||||
}
|
||||
};
|
||||
$scope.filterNormal = true
|
||||
$scope.filterRetry = false
|
||||
$scope.filterDLQ = false
|
||||
$scope.allTopicList = [];
|
||||
$scope.topicShowList = [];
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
$scope.allTopicList = resp.data.topicList.sort();
|
||||
console.log($scope.allTopicList);
|
||||
console.log(JSON.stringify(resp));
|
||||
$scope.showTopicList(1,$scope.allTopicList.length);
|
||||
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 5000});
|
||||
}
|
||||
|
||||
});
|
||||
$scope.filterStr="";
|
||||
$scope.$watch('filterStr', function() {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterNormal', function() {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterRetry', function() {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.$watch('filterDLQ', function() {
|
||||
$scope.filterList(1);
|
||||
});
|
||||
$scope.filterList = function (currentPage) {
|
||||
var lowExceptStr = $scope.filterStr.toLowerCase();
|
||||
var canShowList = [];
|
||||
|
||||
$scope.allTopicList.forEach(function(element) {
|
||||
if($scope.filterByType(element)){
|
||||
if (element.toLowerCase().indexOf(lowExceptStr) != -1){
|
||||
canShowList.push(element);
|
||||
}
|
||||
}
|
||||
});
|
||||
$scope.paginationConf.totalItems =canShowList.length;
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>canShowList.length?canShowList.length:from + perPage;
|
||||
$scope.topicShowList = canShowList.slice(from, to);
|
||||
};
|
||||
|
||||
$scope.filterByType = function(str){
|
||||
if($scope.filterRetry){
|
||||
if(str.startsWith("%R")){
|
||||
return true
|
||||
}
|
||||
}
|
||||
if($scope.filterDLQ){
|
||||
if(str.startsWith("%D")){
|
||||
return true
|
||||
}
|
||||
}
|
||||
if($scope.filterNormal){
|
||||
if(str.startsWith("%") == false){
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
$scope.showTopicList = function (currentPage,totalItem) {
|
||||
if($scope.filterStr != ""){
|
||||
$scope.filterList(currentPage);
|
||||
return;
|
||||
}
|
||||
var perPage = $scope.paginationConf.itemsPerPage;
|
||||
var from = (currentPage - 1) * perPage;
|
||||
var to = (from + perPage)>totalItem?totalItem:from + perPage;
|
||||
console.log($scope.allTopicList);
|
||||
console.log(from)
|
||||
console.log(to)
|
||||
$scope.topicShowList = $scope.allTopicList.slice(from, to);
|
||||
$scope.paginationConf.totalItems = totalItem ;
|
||||
console.log($scope.topicShowList)
|
||||
console.log($scope.paginationConf.totalItems)
|
||||
$scope.filterList(currentPage);
|
||||
};
|
||||
$scope.deleteTopic= function (topic) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/deleteTopic.do",
|
||||
params:{
|
||||
topic:topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
Notification.info({message: "delete success!", delay: 2000});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.statsView = function (topic) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/stats.query",
|
||||
params: {topic: topic}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(JSON.stringify(resp));
|
||||
ngDialog.open({
|
||||
template: 'statsViewDialog',
|
||||
trapFocus:false,
|
||||
data:{
|
||||
topic:topic,
|
||||
statsData:resp.data
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
};
|
||||
$scope.routerView = function (topic) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/route.query",
|
||||
params: {topic: topic}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(JSON.stringify(resp));
|
||||
ngDialog.open({
|
||||
template: 'routerViewDialog',
|
||||
controller: 'routerViewDialogController',
|
||||
trapFocus:false,
|
||||
data:{
|
||||
topic:topic,
|
||||
routeData:resp.data
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
$scope.consumerView = function (topic) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/queryConsumerByTopic.query",
|
||||
params: {topic: topic}
|
||||
}).success(function (resp) {
|
||||
if (resp.status == 0) {
|
||||
console.log(JSON.stringify(resp));
|
||||
ngDialog.open({
|
||||
template: 'consumerViewDialog',
|
||||
data:{
|
||||
topic:topic,
|
||||
consumerData:resp.data,
|
||||
consumerGroupCount:Object.keys(resp.data).length
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
};
|
||||
$scope.openDeleteTopicDialog = function (topic) {
|
||||
ngDialog.open({
|
||||
template: 'deleteTopicDialog',
|
||||
controller: 'deleteTopicDialogController',
|
||||
data:{
|
||||
topic:topic,
|
||||
consumerData:"asd"
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openConsumerResetOffsetDialog = function (topic) {
|
||||
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/queryTopicConsumerInfo.query",
|
||||
params:{
|
||||
topic:topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
if(resp.data.groupList == null){
|
||||
Notification.error({message: "don't have consume group!", delay: 2000});
|
||||
return
|
||||
}
|
||||
ngDialog.open({
|
||||
template: 'consumerResetOffsetDialog',
|
||||
controller: 'consumerResetOffsetDialogController',
|
||||
data:{
|
||||
topic: topic,
|
||||
selectedConsumerGroup:[],
|
||||
allConsumerGroupList:resp.data.groupList
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
$scope.openSendTopicMessageDialog = function (topic) {
|
||||
ngDialog.open({
|
||||
template: 'sendTopicMessageDialog',
|
||||
controller: 'sendTopicMessageDialogController',
|
||||
data: {
|
||||
topic: topic
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openUpdateDialog = function (topic) {
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "topic/examineTopicConfig.query",
|
||||
params:{
|
||||
topic:topic
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
$scope.openCreateOrUpdateDialog(resp.data);
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.openCreateOrUpdateDialog = function (request) {
|
||||
var bIsUpdate = true;
|
||||
|
||||
if(request == null){
|
||||
request = [{
|
||||
writeQueueNums:16,
|
||||
readQueueNums:16,
|
||||
perm:6,
|
||||
order:false,
|
||||
topicName:"",
|
||||
brokerNameList:[]
|
||||
}];
|
||||
bIsUpdate = false;
|
||||
}
|
||||
$http({
|
||||
method: "GET",
|
||||
url: "cluster/list.query"
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
console.log(resp);
|
||||
ngDialog.open({
|
||||
template: 'topicModifyDialog',
|
||||
controller: 'topicModifyDialogController',
|
||||
data:{
|
||||
topicRequestList:request,
|
||||
allClusterNameList:Object.keys(resp.data.clusterInfo.clusterAddrTable),
|
||||
allBrokerNameList:Object.keys(resp.data.brokerServer),
|
||||
bIsUpdate:bIsUpdate
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.openAddDialog = function () {
|
||||
$scope.openCreateOrUpdateDialog(null);
|
||||
}
|
||||
|
||||
}]);
|
||||
|
||||
module.controller('topicModifyDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.postTopicRequest = function (topicRequestItem) {
|
||||
console.log(topicRequestItem);
|
||||
var request = JSON.parse(JSON.stringify(topicRequestItem));
|
||||
console.log(request);
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/createOrUpdate.do",
|
||||
data:request
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
Notification.info({message: "success!", delay: 2000});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
);
|
||||
module.controller('consumerResetOffsetDialogController',['$scope', 'ngDialog', '$http','Notification', function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.timepicker = {};
|
||||
$scope.timepicker.date = moment().format('YYYY-MM-DD HH:mm');
|
||||
$scope.timepicker.options = {format: 'YYYY-MM-DD HH:mm', showClear: true};
|
||||
$scope.resetOffset = function () {
|
||||
console.log($scope.timepicker.date);
|
||||
console.log($scope.timepicker.date.valueOf());
|
||||
console.log($scope.ngDialogData.selectedConsumerGroup);
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "consumer/resetOffset.do",
|
||||
data: {
|
||||
resetTime: $scope.timepicker.date.valueOf(),
|
||||
consumerGroupList: $scope.ngDialogData.selectedConsumerGroup,
|
||||
topic:$scope.ngDialogData.topic,
|
||||
force:true
|
||||
}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
ngDialog.open({
|
||||
template: 'resetOffsetResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
}
|
||||
}]
|
||||
);
|
||||
|
||||
module.controller('sendTopicMessageDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.sendTopicMessage = {
|
||||
topic: $scope.ngDialogData.topic,
|
||||
key: "key",
|
||||
tag:"tag",
|
||||
messageBody:"messageBody"
|
||||
};
|
||||
$scope.send = function () {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/sendTopicMessage.do",
|
||||
data: $scope.sendTopicMessage
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
ngDialog.open({
|
||||
template: 'sendResultDialog',
|
||||
data:{
|
||||
result:resp.data
|
||||
}
|
||||
});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
}
|
||||
}]
|
||||
|
||||
);
|
||||
|
||||
module.controller('routerViewDialogController', ['$scope', 'ngDialog', '$http','Notification',function ($scope, ngDialog, $http,Notification) {
|
||||
$scope.deleteTopicByBroker = function (broker) {
|
||||
$http({
|
||||
method: "POST",
|
||||
url: "topic/deleteTopicByBroker.do",
|
||||
params: {brokerName:broker.brokerName,topic:$scope.ngDialogData.topic}
|
||||
}).success(function (resp) {
|
||||
if(resp.status ==0){
|
||||
Notification.info({message: "delete success", delay: 2000});
|
||||
}else {
|
||||
Notification.error({message: resp.errMsg, delay: 2000});
|
||||
}
|
||||
})
|
||||
};
|
||||
}]
|
||||
|
||||
);
|
||||
32
src/main/resources/static/style/animate.css
vendored
Normal file
32
src/main/resources/static/style/animate.css
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
.view {
|
||||
}
|
||||
|
||||
.view .ng-enter {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.view .ng-leave {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity .2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.ng-hide-add {
|
||||
transform: rotateZ(0);
|
||||
transform-origin: right;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.ng-hide-add.ng-hide-add-active {
|
||||
transform: rotateZ(-135deg);
|
||||
}
|
||||
|
||||
.ng-hide-remove {
|
||||
transform: rotateY(90deg);
|
||||
transform-origin: left;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.ng-hide-remove.ng-hide-remove-active {
|
||||
transform: rotateY(0);
|
||||
}
|
||||
274
src/main/resources/static/style/app.css
Normal file
274
src/main/resources/static/style/app.css
Normal file
@@ -0,0 +1,274 @@
|
||||
/* Write your styles */
|
||||
.scrollTo {
|
||||
position: fixed;
|
||||
right: 50px;
|
||||
bottom: 70px;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/*.icon_u_c{background:url(/app/img/icon_user_center.png) no-repeat left top;}*/
|
||||
.icon {
|
||||
float: left;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
margin: 10px 18px 0 5px;
|
||||
}
|
||||
.icon_alipay {
|
||||
background:url("/style/img/alipay.png") no-repeat left top;
|
||||
height:100px;
|
||||
width: 100px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.icon_alipay_active {
|
||||
background:url("/style/img/alipay_active.png") no-repeat left top;
|
||||
height:100px;
|
||||
width: 100px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.icon_weipay {
|
||||
background:url("/style/img/weipay.png") no-repeat left top;
|
||||
height:100px;
|
||||
width: 100px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.icon_weipay_active {
|
||||
background:url("/style/img/weipay_active.png") no-repeat left top;
|
||||
height:100px;
|
||||
width: 100px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.round {
|
||||
background:#5bc0de;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
border-radius:5px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
|
||||
/* ANIMATIONS
|
||||
============================================================================= */
|
||||
|
||||
/* leaving animations ----------------------------------------- */
|
||||
/* rotate and fall */
|
||||
@keyframes rotateFall {
|
||||
0% { transform: rotateZ(0deg); }
|
||||
20% { transform: rotateZ(10deg); animation-timing-function: ease-out; }
|
||||
40% { transform: rotateZ(17deg); }
|
||||
60% { transform: rotateZ(16deg); }
|
||||
100% { transform: translateY(100%) rotateZ(17deg); }
|
||||
}
|
||||
|
||||
/* slide in from the bottom */
|
||||
@keyframes slideOutLeft {
|
||||
to { transform: translateX(-100%); }
|
||||
}
|
||||
|
||||
/*!* rotate out newspaper *!*/
|
||||
@keyframes rotateOutNewspaper {
|
||||
to { transform: translateZ(-3000px) rotateZ(360deg); opacity: 0; }
|
||||
}
|
||||
|
||||
/* entering animations --------------------------------------- */
|
||||
/* scale up */
|
||||
@keyframes scaleUp {
|
||||
from { opacity: 0.3; -webkit-transform: scale(0.8); }
|
||||
}
|
||||
|
||||
/* slide in from the right */
|
||||
@keyframes slideInRight {
|
||||
from { transform:translateX(100%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
/* slide in from the bottom */
|
||||
@keyframes slideInUp {
|
||||
from { transform:translateY(100%); }
|
||||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
.ng-enter { animation: scaleUp 0.5s both ease-in; z-index: 8888; }
|
||||
.ng-leave { animation: slideOutLeft 0.2s both ease-in; z-index: 9999; }
|
||||
|
||||
|
||||
|
||||
|
||||
.managerList {
|
||||
list-style:none;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.managerList > li {
|
||||
width: 95%;
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
.managerList ul {
|
||||
list-style-type:none;
|
||||
float:left;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.managerList li {
|
||||
list-style-type:none;
|
||||
float:left;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.managerList .head {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.managerList .footer {
|
||||
width: 90%;
|
||||
margin-top: 10px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.managerList .checkbox {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.managerList .liCheck {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.managerList .liContent {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.managerList .preview {
|
||||
width: 50%;
|
||||
margin-left: 50px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.managerList .logo {
|
||||
margin-top: 9px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.managerList .content{
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.managerList .content ul{
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.managerList .butt{
|
||||
/*width: 20%;*/
|
||||
}
|
||||
|
||||
.badgeCustomer {
|
||||
background-color:#f44336;
|
||||
margin-top:23px;
|
||||
}
|
||||
|
||||
.chatPanel {
|
||||
width: 90%;
|
||||
float: left;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
|
||||
float: right;
|
||||
}
|
||||
|
||||
.chatPanel:after{
|
||||
position: absolute;
|
||||
top: 27px;
|
||||
right: -14px;
|
||||
display: inline-block;
|
||||
border-top: 14px solid transparent;
|
||||
border-left: 14px solid #fff;
|
||||
border-right: 0 solid #fff;
|
||||
border-bottom: 14px solid transparent;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
.chatPanel:before{
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
right: -15px;
|
||||
display: inline-block;
|
||||
border-top: 15px solid transparent;
|
||||
border-left: 15px solid #ccc;
|
||||
border-right: 0 solid #ccc;
|
||||
border-bottom: 15px solid transparent;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
.chatPanel-left {
|
||||
float: left;
|
||||
}
|
||||
.chatPanel-left:before {
|
||||
border-left-width: 0;
|
||||
border-right-width: 15px;
|
||||
left: -15px;
|
||||
right: auto;
|
||||
}
|
||||
.chatPanel-left:after {
|
||||
border-left-width: 0;
|
||||
border-right-width: 14px;
|
||||
left: -14px;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.xxs-avatar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
|
||||
.xs-avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.md-avatar {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.lg-avatar {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.text-truncate {
|
||||
word-wrap: normal;
|
||||
/* for IE */
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.text-yellow {
|
||||
color:#FFCC66;
|
||||
}
|
||||
|
||||
.label-xl{
|
||||
font-size:150%;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.limit_height{
|
||||
height: 600px;overflow-y:auto;overflow-x:hidden;
|
||||
}
|
||||
|
||||
.table.text-middle>tbody>tr>td,.table.text-middle>tbody>tr>th{
|
||||
vertical-align: middle;
|
||||
}
|
||||
752
src/main/resources/static/style/btn.css
Normal file
752
src/main/resources/static/style/btn.css
Normal file
@@ -0,0 +1,752 @@
|
||||
|
||||
.btn,
|
||||
.navbar .navbar-nav > li > a.btn {
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
padding: 12px 30px;
|
||||
margin: 10px 1px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0;
|
||||
will-change: box-shadow, transform;
|
||||
transition: box-shadow 0.2s cubic-bezier(0.4, 0, 1, 1), background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
.btn::-moz-focus-inner,
|
||||
.navbar .navbar-nav > li > a.btn::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
.btn, .btn.btn-default,
|
||||
.navbar .navbar-nav > li > a.btn,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default {
|
||||
box-shadow: 0 2px 2px 0 rgba(153, 153, 153, 0.14), 0 3px 1px -2px rgba(153, 153, 153, 0.2), 0 1px 5px 0 rgba(153, 153, 153, 0.12);
|
||||
}
|
||||
.btn, .btn:hover, .btn:focus, .btn:active, .btn.active, .btn:active:focus, .btn:active:hover, .btn.active:focus, .btn.active:hover, .open > .btn.dropdown-toggle, .open > .btn.dropdown-toggle:focus, .open > .btn.dropdown-toggle:hover, .btn.btn-default, .btn.btn-default:hover, .btn.btn-default:focus, .btn.btn-default:active, .btn.btn-default.active, .btn.btn-default:active:focus, .btn.btn-default:active:hover, .btn.btn-default.active:focus, .btn.btn-default.active:hover, .open > .btn.btn-default.dropdown-toggle, .open > .btn.btn-default.dropdown-toggle:focus, .open > .btn.btn-default.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn,
|
||||
.navbar .navbar-nav > li > a.btn:hover,
|
||||
.navbar .navbar-nav > li > a.btn:focus,
|
||||
.navbar .navbar-nav > li > a.btn:active,
|
||||
.navbar .navbar-nav > li > a.btn.active,
|
||||
.navbar .navbar-nav > li > a.btn:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.dropdown-toggle:hover {
|
||||
background-color: #999999;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn:focus, .btn:active, .btn:hover, .btn.btn-default:focus, .btn.btn-default:active, .btn.btn-default:hover,
|
||||
.navbar .navbar-nav > li > a.btn:focus,
|
||||
.navbar .navbar-nav > li > a.btn:active,
|
||||
.navbar .navbar-nav > li > a.btn:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:hover {
|
||||
box-shadow: 0 14px 26px -12px rgba(153, 153, 153, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(153, 153, 153, 0.2);
|
||||
}
|
||||
.btn.disabled, .btn.disabled:hover, .btn.disabled:focus, .btn.disabled.focus, .btn.disabled:active, .btn.disabled.active, .btn:disabled, .btn:disabled:hover, .btn:disabled:focus, .btn:disabled.focus, .btn:disabled:active, .btn:disabled.active, .btn[disabled], .btn[disabled]:hover, .btn[disabled]:focus, .btn[disabled].focus, .btn[disabled]:active, .btn[disabled].active, fieldset[disabled] .btn, fieldset[disabled] .btn:hover, fieldset[disabled] .btn:focus, fieldset[disabled] .btn.focus, fieldset[disabled] .btn:active, fieldset[disabled] .btn.active, .btn.btn-default.disabled, .btn.btn-default.disabled:hover, .btn.btn-default.disabled:focus, .btn.btn-default.disabled.focus, .btn.btn-default.disabled:active, .btn.btn-default.disabled.active, .btn.btn-default:disabled, .btn.btn-default:disabled:hover, .btn.btn-default:disabled:focus, .btn.btn-default:disabled.focus, .btn.btn-default:disabled:active, .btn.btn-default:disabled.active, .btn.btn-default[disabled], .btn.btn-default[disabled]:hover, .btn.btn-default[disabled]:focus, .btn.btn-default[disabled].focus, .btn.btn-default[disabled]:active, .btn.btn-default[disabled].active, fieldset[disabled] .btn.btn-default, fieldset[disabled] .btn.btn-default:hover, fieldset[disabled] .btn.btn-default:focus, fieldset[disabled] .btn.btn-default.focus, fieldset[disabled] .btn.btn-default:active, fieldset[disabled] .btn.btn-default.active,
|
||||
.navbar .navbar-nav > li > a.btn.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn:disabled,
|
||||
.navbar .navbar-nav > li > a.btn:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn[disabled],
|
||||
.navbar .navbar-nav > li > a.btn[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default[disabled],
|
||||
.navbar .navbar-nav > li > a.btn.btn-default[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-default, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-default:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-simple, .btn.btn-default.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple {
|
||||
background-color: transparent;
|
||||
color: #999999;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-simple:hover, .btn.btn-simple:focus, .btn.btn-simple:active, .btn.btn-default.btn-simple:hover, .btn.btn-default.btn-simple:focus, .btn.btn-default.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-default.btn-simple:active {
|
||||
background-color: transparent;
|
||||
color: #999999;
|
||||
}
|
||||
.btn.btn-primary,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary {
|
||||
box-shadow: 0 2px 2px 0 rgba(156, 39, 176, 0.14), 0 3px 1px -2px rgba(156, 39, 176, 0.2), 0 1px 5px 0 rgba(156, 39, 176, 0.12);
|
||||
}
|
||||
.btn.btn-primary, .btn.btn-primary:hover, .btn.btn-primary:focus, .btn.btn-primary:active, .btn.btn-primary.active, .btn.btn-primary:active:focus, .btn.btn-primary:active:hover, .btn.btn-primary.active:focus, .btn.btn-primary.active:hover, .open > .btn.btn-primary.dropdown-toggle, .open > .btn.btn-primary.dropdown-toggle:focus, .open > .btn.btn-primary.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.dropdown-toggle:hover {
|
||||
background-color: #9c27b0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn.btn-primary:focus, .btn.btn-primary:active, .btn.btn-primary:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:hover {
|
||||
box-shadow: 0 14px 26px -12px rgba(156, 39, 176, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(156, 39, 176, 0.2);
|
||||
}
|
||||
.btn.btn-primary.disabled, .btn.btn-primary.disabled:hover, .btn.btn-primary.disabled:focus, .btn.btn-primary.disabled.focus, .btn.btn-primary.disabled:active, .btn.btn-primary.disabled.active, .btn.btn-primary:disabled, .btn.btn-primary:disabled:hover, .btn.btn-primary:disabled:focus, .btn.btn-primary:disabled.focus, .btn.btn-primary:disabled:active, .btn.btn-primary:disabled.active, .btn.btn-primary[disabled], .btn.btn-primary[disabled]:hover, .btn.btn-primary[disabled]:focus, .btn.btn-primary[disabled].focus, .btn.btn-primary[disabled]:active, .btn.btn-primary[disabled].active, fieldset[disabled] .btn.btn-primary, fieldset[disabled] .btn.btn-primary:hover, fieldset[disabled] .btn.btn-primary:focus, fieldset[disabled] .btn.btn-primary.focus, fieldset[disabled] .btn.btn-primary:active, fieldset[disabled] .btn.btn-primary.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary[disabled],
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-primary.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple {
|
||||
background-color: transparent;
|
||||
color: #9c27b0;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-primary.btn-simple:hover, .btn.btn-primary.btn-simple:focus, .btn.btn-primary.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-primary.btn-simple:active {
|
||||
background-color: transparent;
|
||||
color: #9c27b0;
|
||||
}
|
||||
.btn.btn-info,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info {
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 188, 212, 0.14), 0 3px 1px -2px rgba(0, 188, 212, 0.2), 0 1px 5px 0 rgba(0, 188, 212, 0.12);
|
||||
}
|
||||
.btn.btn-info, .btn.btn-info:hover, .btn.btn-info:focus, .btn.btn-info:active, .btn.btn-info.active, .btn.btn-info:active:focus, .btn.btn-info:active:hover, .btn.btn-info.active:focus, .btn.btn-info.active:hover, .open > .btn.btn-info.dropdown-toggle, .open > .btn.btn-info.dropdown-toggle:focus, .open > .btn.btn-info.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.dropdown-toggle:hover {
|
||||
background-color: #00bcd4;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn.btn-info:focus, .btn.btn-info:active, .btn.btn-info:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:hover {
|
||||
box-shadow: 0 14px 26px -12px rgba(0, 188, 212, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 188, 212, 0.2);
|
||||
}
|
||||
.btn.btn-info.disabled, .btn.btn-info.disabled:hover, .btn.btn-info.disabled:focus, .btn.btn-info.disabled.focus, .btn.btn-info.disabled:active, .btn.btn-info.disabled.active, .btn.btn-info:disabled, .btn.btn-info:disabled:hover, .btn.btn-info:disabled:focus, .btn.btn-info:disabled.focus, .btn.btn-info:disabled:active, .btn.btn-info:disabled.active, .btn.btn-info[disabled], .btn.btn-info[disabled]:hover, .btn.btn-info[disabled]:focus, .btn.btn-info[disabled].focus, .btn.btn-info[disabled]:active, .btn.btn-info[disabled].active, fieldset[disabled] .btn.btn-info, fieldset[disabled] .btn.btn-info:hover, fieldset[disabled] .btn.btn-info:focus, fieldset[disabled] .btn.btn-info.focus, fieldset[disabled] .btn.btn-info:active, fieldset[disabled] .btn.btn-info.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info[disabled],
|
||||
.navbar .navbar-nav > li > a.btn.btn-info[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-info, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-info:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-info.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple {
|
||||
background-color: transparent;
|
||||
color: #00bcd4;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-info.btn-simple:hover, .btn.btn-info.btn-simple:focus, .btn.btn-info.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-info.btn-simple:active {
|
||||
background-color: transparent;
|
||||
color: #00bcd4;
|
||||
}
|
||||
.btn.btn-success,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success {
|
||||
box-shadow: 0 2px 2px 0 rgba(76, 175, 80, 0.14), 0 3px 1px -2px rgba(76, 175, 80, 0.2), 0 1px 5px 0 rgba(76, 175, 80, 0.12);
|
||||
}
|
||||
.btn.btn-success, .btn.btn-success:hover, .btn.btn-success:focus, .btn.btn-success:active, .btn.btn-success.active, .btn.btn-success:active:focus, .btn.btn-success:active:hover, .btn.btn-success.active:focus, .btn.btn-success.active:hover, .open > .btn.btn-success.dropdown-toggle, .open > .btn.btn-success.dropdown-toggle:focus, .open > .btn.btn-success.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.dropdown-toggle:hover {
|
||||
background-color: #4caf50;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn.btn-success:focus, .btn.btn-success:active, .btn.btn-success:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:hover {
|
||||
box-shadow: 0 14px 26px -12px rgba(76, 175, 80, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(76, 175, 80, 0.2);
|
||||
}
|
||||
.btn.btn-success.disabled, .btn.btn-success.disabled:hover, .btn.btn-success.disabled:focus, .btn.btn-success.disabled.focus, .btn.btn-success.disabled:active, .btn.btn-success.disabled.active, .btn.btn-success:disabled, .btn.btn-success:disabled:hover, .btn.btn-success:disabled:focus, .btn.btn-success:disabled.focus, .btn.btn-success:disabled:active, .btn.btn-success:disabled.active, .btn.btn-success[disabled], .btn.btn-success[disabled]:hover, .btn.btn-success[disabled]:focus, .btn.btn-success[disabled].focus, .btn.btn-success[disabled]:active, .btn.btn-success[disabled].active, fieldset[disabled] .btn.btn-success, fieldset[disabled] .btn.btn-success:hover, fieldset[disabled] .btn.btn-success:focus, fieldset[disabled] .btn.btn-success.focus, fieldset[disabled] .btn.btn-success:active, fieldset[disabled] .btn.btn-success.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success[disabled],
|
||||
.navbar .navbar-nav > li > a.btn.btn-success[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-success, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-success:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-success.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple {
|
||||
background-color: transparent;
|
||||
color: #4caf50;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-success.btn-simple:hover, .btn.btn-success.btn-simple:focus, .btn.btn-success.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-success.btn-simple:active {
|
||||
background-color: transparent;
|
||||
color: #4caf50;
|
||||
}
|
||||
.btn.btn-warning,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning {
|
||||
box-shadow: 0 2px 2px 0 rgba(255, 152, 0, 0.14), 0 3px 1px -2px rgba(255, 152, 0, 0.2), 0 1px 5px 0 rgba(255, 152, 0, 0.12);
|
||||
}
|
||||
.btn.btn-warning, .btn.btn-warning:hover, .btn.btn-warning:focus, .btn.btn-warning:active, .btn.btn-warning.active, .btn.btn-warning:active:focus, .btn.btn-warning:active:hover, .btn.btn-warning.active:focus, .btn.btn-warning.active:hover, .open > .btn.btn-warning.dropdown-toggle, .open > .btn.btn-warning.dropdown-toggle:focus, .open > .btn.btn-warning.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.dropdown-toggle:hover {
|
||||
background-color: #ff9800;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn.btn-warning:focus, .btn.btn-warning:active, .btn.btn-warning:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:hover {
|
||||
box-shadow: 0 14px 26px -12px rgba(255, 152, 0, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(255, 152, 0, 0.2);
|
||||
}
|
||||
.btn.btn-warning.disabled, .btn.btn-warning.disabled:hover, .btn.btn-warning.disabled:focus, .btn.btn-warning.disabled.focus, .btn.btn-warning.disabled:active, .btn.btn-warning.disabled.active, .btn.btn-warning:disabled, .btn.btn-warning:disabled:hover, .btn.btn-warning:disabled:focus, .btn.btn-warning:disabled.focus, .btn.btn-warning:disabled:active, .btn.btn-warning:disabled.active, .btn.btn-warning[disabled], .btn.btn-warning[disabled]:hover, .btn.btn-warning[disabled]:focus, .btn.btn-warning[disabled].focus, .btn.btn-warning[disabled]:active, .btn.btn-warning[disabled].active, fieldset[disabled] .btn.btn-warning, fieldset[disabled] .btn.btn-warning:hover, fieldset[disabled] .btn.btn-warning:focus, fieldset[disabled] .btn.btn-warning.focus, fieldset[disabled] .btn.btn-warning:active, fieldset[disabled] .btn.btn-warning.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning[disabled],
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-warning.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple {
|
||||
background-color: transparent;
|
||||
color: #ff9800;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-warning.btn-simple:hover, .btn.btn-warning.btn-simple:focus, .btn.btn-warning.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-warning.btn-simple:active {
|
||||
background-color: transparent;
|
||||
color: #ff9800;
|
||||
}
|
||||
.btn.btn-danger,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger {
|
||||
box-shadow: 0 2px 2px 0 rgba(244, 67, 54, 0.14), 0 3px 1px -2px rgba(244, 67, 54, 0.2), 0 1px 5px 0 rgba(244, 67, 54, 0.12);
|
||||
}
|
||||
.btn.btn-danger, .btn.btn-danger:hover, .btn.btn-danger:focus, .btn.btn-danger:active, .btn.btn-danger.active, .btn.btn-danger:active:focus, .btn.btn-danger:active:hover, .btn.btn-danger.active:focus, .btn.btn-danger.active:hover, .open > .btn.btn-danger.dropdown-toggle, .open > .btn.btn-danger.dropdown-toggle:focus, .open > .btn.btn-danger.dropdown-toggle:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:active:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.active:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.active:hover, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle:focus, .open >
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.dropdown-toggle:hover {
|
||||
background-color: #f44336;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn.btn-danger:focus, .btn.btn-danger:active, .btn.btn-danger:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:hover {
|
||||
box-shadow: 0 14px 26px -12px rgba(244, 67, 54, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(244, 67, 54, 0.2);
|
||||
}
|
||||
.btn.btn-danger.disabled, .btn.btn-danger.disabled:hover, .btn.btn-danger.disabled:focus, .btn.btn-danger.disabled.focus, .btn.btn-danger.disabled:active, .btn.btn-danger.disabled.active, .btn.btn-danger:disabled, .btn.btn-danger:disabled:hover, .btn.btn-danger:disabled:focus, .btn.btn-danger:disabled.focus, .btn.btn-danger:disabled:active, .btn.btn-danger:disabled.active, .btn.btn-danger[disabled], .btn.btn-danger[disabled]:hover, .btn.btn-danger[disabled]:focus, .btn.btn-danger[disabled].focus, .btn.btn-danger[disabled]:active, .btn.btn-danger[disabled].active, fieldset[disabled] .btn.btn-danger, fieldset[disabled] .btn.btn-danger:hover, fieldset[disabled] .btn.btn-danger:focus, fieldset[disabled] .btn.btn-danger.focus, fieldset[disabled] .btn.btn-danger:active, fieldset[disabled] .btn.btn-danger.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:disabled,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:disabled:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:disabled:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:disabled.focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:disabled:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:disabled.active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger[disabled],
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger[disabled].focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger[disabled]:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger[disabled].active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:hover, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.focus, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger:active, fieldset[disabled]
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.active {
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-danger.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple {
|
||||
background-color: transparent;
|
||||
color: #f44336;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-danger.btn-simple:hover, .btn.btn-danger.btn-simple:focus, .btn.btn-danger.btn-simple:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-danger.btn-simple:active {
|
||||
background-color: transparent;
|
||||
color: #f44336;
|
||||
}
|
||||
.btn.btn-white, .btn.btn-white:focus, .btn.btn-white:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-white,
|
||||
.navbar .navbar-nav > li > a.btn.btn-white:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-white:hover {
|
||||
background-color: #FFFFFF;
|
||||
color: #999999;
|
||||
}
|
||||
.btn.btn-white.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-white.btn-simple {
|
||||
color: #FFFFFF;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-facebook,
|
||||
.navbar .navbar-nav > li > a.btn.btn-facebook {
|
||||
background-color: #3b5998;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 2px 0 rgba(59, 89, 152, 0.14), 0 3px 1px -2px rgba(59, 89, 152, 0.2), 0 1px 5px 0 rgba(59, 89, 152, 0.12);
|
||||
}
|
||||
.btn.btn-facebook:focus, .btn.btn-facebook:active, .btn.btn-facebook:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-facebook:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-facebook:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-facebook:hover {
|
||||
background-color: #3b5998;
|
||||
color: #fff;
|
||||
box-shadow: 0 14px 26px -12px rgba(59, 89, 152, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(59, 89, 152, 0.2);
|
||||
}
|
||||
.btn.btn-facebook.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-facebook.btn-simple {
|
||||
color: #3b5998;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-twitter,
|
||||
.navbar .navbar-nav > li > a.btn.btn-twitter {
|
||||
background-color: #55acee;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 2px 0 rgba(85, 172, 238, 0.14), 0 3px 1px -2px rgba(85, 172, 238, 0.2), 0 1px 5px 0 rgba(85, 172, 238, 0.12);
|
||||
}
|
||||
.btn.btn-twitter:focus, .btn.btn-twitter:active, .btn.btn-twitter:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-twitter:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-twitter:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-twitter:hover {
|
||||
background-color: #55acee;
|
||||
color: #fff;
|
||||
box-shadow: 0 14px 26px -12px rgba(85, 172, 238, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(85, 172, 238, 0.2);
|
||||
}
|
||||
.btn.btn-twitter.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-twitter.btn-simple {
|
||||
color: #55acee;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-pinterest,
|
||||
.navbar .navbar-nav > li > a.btn.btn-pinterest {
|
||||
background-color: #cc2127;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 2px 0 rgba(204, 33, 39, 0.14), 0 3px 1px -2px rgba(204, 33, 39, 0.2), 0 1px 5px 0 rgba(204, 33, 39, 0.12);
|
||||
}
|
||||
.btn.btn-pinterest:focus, .btn.btn-pinterest:active, .btn.btn-pinterest:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-pinterest:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-pinterest:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-pinterest:hover {
|
||||
background-color: #cc2127;
|
||||
color: #fff;
|
||||
box-shadow: 0 14px 26px -12px rgba(204, 33, 39, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(204, 33, 39, 0.2);
|
||||
}
|
||||
.btn.btn-pinterest.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-pinterest.btn-simple {
|
||||
color: #cc2127;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-google,
|
||||
.navbar .navbar-nav > li > a.btn.btn-google {
|
||||
background-color: #dd4b39;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 2px 0 rgba(221, 75, 57, 0.14), 0 3px 1px -2px rgba(221, 75, 57, 0.2), 0 1px 5px 0 rgba(221, 75, 57, 0.12);
|
||||
}
|
||||
.btn.btn-google:focus, .btn.btn-google:active, .btn.btn-google:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-google:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-google:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-google:hover {
|
||||
background-color: #dd4b39;
|
||||
color: #fff;
|
||||
box-shadow: 0 14px 26px -12px rgba(221, 75, 57, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(221, 75, 57, 0.2);
|
||||
}
|
||||
.btn.btn-google.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-google.btn-simple {
|
||||
color: #dd4b39;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn.btn-instagram,
|
||||
.navbar .navbar-nav > li > a.btn.btn-instagram {
|
||||
background-color: #125688;
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 2px 0 rgba(18, 86, 136, 0.14), 0 3px 1px -2px rgba(18, 86, 136, 0.2), 0 1px 5px 0 rgba(18, 86, 136, 0.12);
|
||||
}
|
||||
.btn.btn-instagram:focus, .btn.btn-instagram:active, .btn.btn-instagram:hover,
|
||||
.navbar .navbar-nav > li > a.btn.btn-instagram:focus,
|
||||
.navbar .navbar-nav > li > a.btn.btn-instagram:active,
|
||||
.navbar .navbar-nav > li > a.btn.btn-instagram:hover {
|
||||
background-color: #125688;
|
||||
color: #fff;
|
||||
box-shadow: 0 14px 26px -12px rgba(18, 86, 136, 0.42), 0 4px 23px 0px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(18, 86, 136, 0.2);
|
||||
}
|
||||
.btn.btn-instagram.btn-simple,
|
||||
.navbar .navbar-nav > li > a.btn.btn-instagram.btn-simple {
|
||||
color: #125688;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn:focus, .btn:active, .btn:active:focus,
|
||||
.navbar .navbar-nav > li > a.btn:focus,
|
||||
.navbar .navbar-nav > li > a.btn:active,
|
||||
.navbar .navbar-nav > li > a.btn:active:focus {
|
||||
outline: 0;
|
||||
}
|
||||
.btn.btn-round,
|
||||
.navbar .navbar-nav > li > a.btn.btn-round {
|
||||
border-radius: 30px;
|
||||
}
|
||||
.btn:not(.btn-just-icon):not(.btn-fab) .fa,
|
||||
.navbar .navbar-nav > li > a.btn:not(.btn-just-icon):not(.btn-fab) .fa {
|
||||
font-size: 18px;
|
||||
margin-top: -2px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.btn.btn-fab,
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab {
|
||||
border-radius: 50%;
|
||||
font-size: 24px;
|
||||
height: 56px;
|
||||
margin: auto;
|
||||
min-width: 56px;
|
||||
width: 56px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
line-height: normal;
|
||||
}
|
||||
.btn.btn-fab .ripple-container,
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab .ripple-container {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.btn.btn-fab.btn-fab-mini, .btn-group-sm .btn.btn-fab,
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini, .btn-group-sm
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab {
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
.btn.btn-fab.btn-fab-mini.material-icons, .btn-group-sm .btn.btn-fab.material-icons,
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini.material-icons, .btn-group-sm
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab.material-icons {
|
||||
top: -3.5px;
|
||||
left: -3.5px;
|
||||
}
|
||||
.btn.btn-fab.btn-fab-mini .material-icons, .btn-group-sm .btn.btn-fab .material-icons,
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab.btn-fab-mini .material-icons, .btn-group-sm
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab .material-icons {
|
||||
font-size: 17px;
|
||||
}
|
||||
.btn.btn-fab i.material-icons,
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab i.material-icons {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-12px, -12px);
|
||||
line-height: 24px;
|
||||
width: 24px;
|
||||
font-size: 24px;
|
||||
}
|
||||
.btn.btn-lg, .btn-group-lg .btn,
|
||||
.navbar .navbar-nav > li > a.btn.btn-lg, .btn-group-lg
|
||||
.navbar .navbar-nav > li > a.btn {
|
||||
font-size: 14px;
|
||||
padding: 18px 36px;
|
||||
}
|
||||
.btn.btn-sm, .btn-group-sm .btn,
|
||||
.navbar .navbar-nav > li > a.btn.btn-sm, .btn-group-sm
|
||||
.navbar .navbar-nav > li > a.btn {
|
||||
padding: 5px 20px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.btn.btn-xs, .btn-group-xs .btn,
|
||||
.navbar .navbar-nav > li > a.btn.btn-xs, .btn-group-xs
|
||||
.navbar .navbar-nav > li > a.btn {
|
||||
padding: 4px 15px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.btn.btn-just-icon,
|
||||
.navbar .navbar-nav > li > a.btn.btn-just-icon {
|
||||
font-size: 20px;
|
||||
padding: 12px 12px;
|
||||
line-height: 1em;
|
||||
}
|
||||
.btn.btn-just-icon i,
|
||||
.navbar .navbar-nav > li > a.btn.btn-just-icon i {
|
||||
width: 20px;
|
||||
}
|
||||
.btn.btn-just-icon.btn-lg,
|
||||
.navbar .navbar-nav > li > a.btn.btn-just-icon.btn-lg {
|
||||
font-size: 22px;
|
||||
padding: 13px 18px;
|
||||
}
|
||||
|
||||
.btn .material-icons {
|
||||
vertical-align: middle;
|
||||
font-size: 17px;
|
||||
top: -1px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navbar .navbar-nav > li > a.btn {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.navbar .navbar-nav > li > a.btn.btn-fab {
|
||||
margin: 5px 2px;
|
||||
}
|
||||
.navbar .navbar-nav > li > a:not(.btn) .material-icons {
|
||||
margin-top: -3px;
|
||||
top: 0px;
|
||||
position: relative;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.navbar .navbar-nav > li > .profile-photo {
|
||||
margin: 5px 2px;
|
||||
}
|
||||
|
||||
.navbar-default:not(.navbar-transparent) .navbar-nav > li > a.btn.btn-white.btn-simple {
|
||||
color: #555555;
|
||||
}
|
||||
|
||||
.btn-group,
|
||||
.btn-group-vertical {
|
||||
position: relative;
|
||||
margin: 10px 1px;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn, .btn-group.open > .dropdown-toggle.btn.btn-default,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-default {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-inverse,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-inverse {
|
||||
background-color: #212121;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-primary,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-primary {
|
||||
background-color: #9c27b0;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-success,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-success {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-info,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-info {
|
||||
background-color: #00bcd4;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-warning,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-warning {
|
||||
background-color: #ff9800;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-danger,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-danger {
|
||||
background-color: #f44336;
|
||||
}
|
||||
.btn-group.open > .dropdown-toggle.btn.btn-rose,
|
||||
.btn-group-vertical.open > .dropdown-toggle.btn.btn-rose {
|
||||
background-color: #e91e63;
|
||||
}
|
||||
.btn-group .dropdown-menu,
|
||||
.btn-group-vertical .dropdown-menu {
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
.btn-group.btn-group-raised,
|
||||
.btn-group-vertical.btn-group-raised {
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
.btn-group .btn + .btn,
|
||||
.btn-group .btn,
|
||||
.btn-group .btn:active,
|
||||
.btn-group .btn-group,
|
||||
.btn-group-vertical .btn + .btn,
|
||||
.btn-group-vertical .btn,
|
||||
.btn-group-vertical .btn:active,
|
||||
.btn-group-vertical .btn-group {
|
||||
margin: 0;
|
||||
}
|
||||
BIN
src/main/resources/static/style/img/alipay.png
Normal file
BIN
src/main/resources/static/style/img/alipay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/main/resources/static/style/img/alipay_active.png
Normal file
BIN
src/main/resources/static/style/img/alipay_active.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
src/main/resources/static/style/img/icon_user_center.png
Normal file
BIN
src/main/resources/static/style/img/icon_user_center.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
src/main/resources/static/style/img/weipay.png
Normal file
BIN
src/main/resources/static/style/img/weipay.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/main/resources/static/style/img/weipay_active.png
Normal file
BIN
src/main/resources/static/style/img/weipay_active.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
12
src/main/resources/static/style/login.css
Normal file
12
src/main/resources/static/style/login.css
Normal file
@@ -0,0 +1,12 @@
|
||||
.login-panel{
|
||||
|
||||
}
|
||||
|
||||
.login-panel .qrcode {
|
||||
width: 100%;
|
||||
hight: 100%;
|
||||
}
|
||||
|
||||
.login-panel .validateCode {
|
||||
/*height:20px;*/
|
||||
}
|
||||
420
src/main/resources/static/style/preLoading/main.css
Executable file
420
src/main/resources/static/style/preLoading/main.css
Executable file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
_____ _ _ _ _
|
||||
|_ _| | | | | | | | |
|
||||
| | | |__ __ _| |_ ___ | |_ ___ _ __ ___ __ _| |_ ___ ___ ___
|
||||
| | | '_ \ / _` | __/ _ \ | __/ _ \| '_ ` _ \ / _` | __/ _ \ / _ \/ __|
|
||||
_| |_ | | | | (_| | || __/ | || (_) | | | | | | (_| | || (_) | __/\__ \
|
||||
\___/ |_| |_|\__,_|\__\___| \__\___/|_| |_| |_|\__,_|\__\___/ \___||___/
|
||||
|
||||
Oh nice, welcome to the stylesheet of dreams.
|
||||
It has it all. Classes, ID's, comments...the whole lot:)
|
||||
Enjoy responsibly!
|
||||
@ihatetomatoes
|
||||
|
||||
*/
|
||||
|
||||
/* ==========================================================================
|
||||
Chrome Frame prompt
|
||||
========================================================================== */
|
||||
|
||||
.chromeframe {
|
||||
margin: 0.2em 0;
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 0.2em 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Author's custom styles
|
||||
========================================================================== */
|
||||
p {
|
||||
line-height: 1.33em;
|
||||
color: #7E7E7E;
|
||||
}
|
||||
h1 {
|
||||
color: #EEEEEE;
|
||||
}
|
||||
|
||||
#loader-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
#loader {
|
||||
display: block;
|
||||
position: relative;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: -75px 0 0 -75px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #3498db;
|
||||
|
||||
-webkit-animation: spin 2s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
|
||||
animation: spin 2s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
|
||||
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
#loader:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #e74c3c;
|
||||
|
||||
-webkit-animation: spin 3s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
|
||||
animation: spin 3s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
|
||||
#loader:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
border-radius: 50%;
|
||||
border: 3px solid transparent;
|
||||
border-top-color: #f9c922;
|
||||
|
||||
-webkit-animation: spin 1.5s linear infinite; /* Chrome, Opera 15+, Safari 5+ */
|
||||
animation: spin 1.5s linear infinite; /* Chrome, Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: rotate(0deg); /* IE 9 */
|
||||
transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: rotate(360deg); /* IE 9 */
|
||||
transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
}
|
||||
@keyframes spin {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: rotate(0deg); /* IE 9 */
|
||||
transform: rotate(0deg); /* Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: rotate(360deg); /* IE 9 */
|
||||
transform: rotate(360deg); /* Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 51%;
|
||||
height: 100%;
|
||||
background: #222222;
|
||||
z-index: 1000;
|
||||
-webkit-transform: translateX(0); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: translateX(0); /* IE 9 */
|
||||
transform: translateX(0); /* Firefox 16+, IE 10+, Opera */
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#loader-wrapper .loader-section.section-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
/* Loaded */
|
||||
.loaded #loader-wrapper .loader-section.section-left {
|
||||
-webkit-transform: translateX(-100%); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: translateX(-100%); /* IE 9 */
|
||||
transform: translateX(-100%); /* Firefox 16+, IE 10+, Opera */
|
||||
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.loaded #loader-wrapper .loader-section.section-right {
|
||||
-webkit-transform: translateX(100%); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: translateX(100%); /* IE 9 */
|
||||
transform: translateX(100%); /* Firefox 16+, IE 10+, Opera */
|
||||
|
||||
-webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
|
||||
}
|
||||
|
||||
.loaded #loader {
|
||||
opacity: 0;
|
||||
-webkit-transition: all 0.3s ease-out;
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
.loaded #loader-wrapper {
|
||||
visibility: hidden;
|
||||
|
||||
-webkit-transform: translateY(-100%); /* Chrome, Opera 15+, Safari 3.1+ */
|
||||
-ms-transform: translateY(-100%); /* IE 9 */
|
||||
transform: translateY(-100%); /* Firefox 16+, IE 10+, Opera */
|
||||
|
||||
-webkit-transition: all 0.3s 1s ease-out;
|
||||
transition: all 0.3s 1s ease-out;
|
||||
}
|
||||
|
||||
/* JavaScript Turned Off */
|
||||
.no-js #loader-wrapper {
|
||||
display: none;
|
||||
}
|
||||
.no-js h1 {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 0 auto;
|
||||
padding-bottom: 50px;
|
||||
width: 80%;
|
||||
max-width: 978px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ==========================================================================
|
||||
Helper classes
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Image replacement
|
||||
*/
|
||||
|
||||
.ir {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
/* IE 6/7 fallback */
|
||||
*text-indent: -9999px;
|
||||
}
|
||||
|
||||
.ir:before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 150%;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide from both screenreaders and browsers: h5bp.com/u
|
||||
*/
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide only visually, but have it available for screenreaders: h5bp.com/v
|
||||
*/
|
||||
|
||||
.visuallyhidden {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extends the .visuallyhidden class to allow the element to be focusable
|
||||
* when navigated to via the keyboard: h5bp.com/p
|
||||
*/
|
||||
|
||||
.visuallyhidden.focusable:active,
|
||||
.visuallyhidden.focusable:focus {
|
||||
clip: auto;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
position: static;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide visually and from screenreaders, but maintain layout
|
||||
*/
|
||||
|
||||
.invisible {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clearfix: contain floats
|
||||
*
|
||||
* For modern browsers
|
||||
* 1. The space content is one way to avoid an Opera bug when the
|
||||
* `contenteditable` attribute is included anywhere else in the document.
|
||||
* Otherwise it causes space to appear at the top and bottom of elements
|
||||
* that receive the `clearfix` class.
|
||||
* 2. The use of `table` rather than `block` is only necessary if using
|
||||
* `:before` to contain the top-margins of child elements.
|
||||
*/
|
||||
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
content: " "; /* 1 */
|
||||
display: table; /* 2 */
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/*
|
||||
* For IE 6/7 only
|
||||
* Include this rule to trigger hasLayout and contain floats.
|
||||
*/
|
||||
|
||||
.clearfix {
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
EXAMPLE Media Queries for Responsive Design.
|
||||
These examples override the primary ('mobile first') styles.
|
||||
Modify as content requires.
|
||||
========================================================================== */
|
||||
|
||||
@media only screen and (min-width: 35em) {
|
||||
/* Style adjustments for viewports that meet the condition */
|
||||
}
|
||||
|
||||
@media print,
|
||||
(-o-min-device-pixel-ratio: 5/4),
|
||||
(-webkit-min-device-pixel-ratio: 1.25),
|
||||
(min-resolution: 120dpi) {
|
||||
/* Style adjustments for high resolution devices */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Print styles.
|
||||
Inlined to avoid required HTTP connection: h5bp.com/r
|
||||
========================================================================== */
|
||||
|
||||
@media print {
|
||||
* {
|
||||
background: transparent !important;
|
||||
color: #000 !important; /* Black prints faster: h5bp.com/s */
|
||||
box-shadow: none !important;
|
||||
text-shadow: none !important;
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a[href]:after {
|
||||
content: " (" attr(href) ")";
|
||||
}
|
||||
|
||||
abbr[title]:after {
|
||||
content: " (" attr(title) ")";
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't show links for images, or javascript/internal links
|
||||
*/
|
||||
|
||||
.ir a:after,
|
||||
a[href^="javascript:"]:after,
|
||||
a[href^="#"]:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
pre,
|
||||
blockquote {
|
||||
border: 1px solid #999;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
thead {
|
||||
display: table-header-group; /* h5bp.com/t */
|
||||
}
|
||||
|
||||
tr,
|
||||
img {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
@page {
|
||||
margin: 0.5cm;
|
||||
}
|
||||
|
||||
p,
|
||||
h2,
|
||||
h3 {
|
||||
orphans: 3;
|
||||
widows: 3;
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
}
|
||||
|
||||
.back-link a {
|
||||
color: #4ca340;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px #4ca340 solid;
|
||||
}
|
||||
.back-link a:hover,
|
||||
.back-link a:focus {
|
||||
color: #408536;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px #408536 solid;
|
||||
}
|
||||
h1 {
|
||||
height: 100%;
|
||||
/* The html and body elements cannot have any padding or margin. */
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-size: 32px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.entry-header {
|
||||
text-align: left;
|
||||
margin: 0 auto 50px auto;
|
||||
width: 80%;
|
||||
max-width: 978px;
|
||||
position: relative;
|
||||
z-index: 10001;
|
||||
}
|
||||
#demo-content {
|
||||
padding-top: 100px;
|
||||
}
|
||||
|
||||
/*
|
||||
Ok so you have made it this far, that means you are very keen to on my code.
|
||||
Anyway I don't really mind it. This is a great way to learn so you actually doing the right thing:)
|
||||
Follow me @ihatetomatoes
|
||||
*/
|
||||
527
src/main/resources/static/style/preLoading/normalize.css
vendored
Executable file
527
src/main/resources/static/style/preLoading/normalize.css
vendored
Executable file
@@ -0,0 +1,527 @@
|
||||
/*! normalize.css v1.1.2 | MIT License | git.io/normalize */
|
||||
|
||||
/* ==========================================================================
|
||||
HTML5 display definitions
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Correct `block` display not defined in IE 6/7/8/9 and Firefox 3.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
|
||||
*/
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
video {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent modern browsers from displaying `audio` without controls.
|
||||
* Remove excess height in iOS 5 devices.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 7/8/9, Firefox 3, and Safari 4.
|
||||
* Known issue: no IE 6 support.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Base
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using
|
||||
* `em` units.
|
||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||
* user zoom.
|
||||
*/
|
||||
|
||||
html {
|
||||
font-size: 100%; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `font-family` inconsistency between `textarea` and other form
|
||||
* elements.
|
||||
*/
|
||||
|
||||
html,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address margins handled incorrectly in IE 6/7.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Links
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address `outline` inconsistency between Chrome and other browsers.
|
||||
*/
|
||||
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability when focused and also mouse hovered in all browsers.
|
||||
*/
|
||||
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Typography
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address font sizes and margins set differently in IE 6/7.
|
||||
* Address font sizes within `section` and `article` in Firefox 4+, Safari 5,
|
||||
* and Chrome.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
margin: 0.83em 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.17em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1em;
|
||||
margin: 1.33em 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 0.83em;
|
||||
margin: 1.67em 0;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: 0.67em;
|
||||
margin: 2.33em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 7/8/9, Safari 5, and Chrome.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in Safari 5 and Chrome.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address differences between Firefox and other browsers.
|
||||
* Known issue: no IE 6/7 normalization.
|
||||
*/
|
||||
|
||||
hr {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address styling not present in IE 6/7/8/9.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address margins set differently in IE 6/7.
|
||||
*/
|
||||
|
||||
p,
|
||||
pre {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct font family set oddly in IE 6, Safari 4/5, and Chrome.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: monospace, serif;
|
||||
_font-family: 'courier new', monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Improve readability of pre-formatted text in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
white-space: pre;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address CSS quotes not supported in IE 6/7.
|
||||
*/
|
||||
|
||||
q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address `quotes` property not supported in Safari 4.
|
||||
*/
|
||||
|
||||
q:before,
|
||||
q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent and variable font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Lists
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address margins set differently in IE 6/7.
|
||||
*/
|
||||
|
||||
dl,
|
||||
menu,
|
||||
ol,
|
||||
ul {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin: 0 0 0 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address paddings set differently in IE 6/7.
|
||||
*/
|
||||
|
||||
menu,
|
||||
ol,
|
||||
ul {
|
||||
padding: 0 0 0 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct list images handled incorrectly in IE 7.
|
||||
*/
|
||||
|
||||
nav ul,
|
||||
nav ol {
|
||||
list-style: none;
|
||||
list-style-image: none;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3.
|
||||
* 2. Improve image quality when scaled in IE 7.
|
||||
*/
|
||||
|
||||
img {
|
||||
border: 0; /* 1 */
|
||||
-ms-interpolation-mode: bicubic; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct overflow displayed oddly in IE 9.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Figures
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Correct margin displayed oddly in IE 6/7.
|
||||
*/
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define consistent border, margin, and padding.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
border: 1px solid #c0c0c0;
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct color not being inherited in IE 6/7/8/9.
|
||||
* 2. Correct text not wrapping in Firefox 3.
|
||||
* 3. Correct alignment displayed oddly in IE 6/7.
|
||||
*/
|
||||
|
||||
legend {
|
||||
border: 0; /* 1 */
|
||||
padding: 0;
|
||||
white-space: normal; /* 2 */
|
||||
*margin-left: -7px; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct font size not being inherited in all browsers.
|
||||
* 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5,
|
||||
* and Chrome.
|
||||
* 3. Improve appearance and consistency in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 100%; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
vertical-align: baseline; /* 3 */
|
||||
*vertical-align: middle; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Address Firefox 3+ setting `line-height` on `input` using `!important` in
|
||||
* the UA stylesheet.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||
* All other form control elements do not inherit `text-transform` values.
|
||||
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+.
|
||||
* Correct `select` style inheritance in Firefox 4+ and Opera.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||
* and `video` controls.
|
||||
* 2. Correct inability to style clickable `input` types in iOS.
|
||||
* 3. Improve usability and consistency of cursor style between image-type
|
||||
* `input` and others.
|
||||
* 4. Remove inner spacing in IE 7 without affecting normal text inputs.
|
||||
* Known issue: inner spacing remains in IE 6.
|
||||
*/
|
||||
|
||||
button,
|
||||
html input[type="button"], /* 1 */
|
||||
input[type="reset"],
|
||||
input[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
cursor: pointer; /* 3 */
|
||||
*overflow: visible; /* 4 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-set default cursor for disabled elements.
|
||||
*/
|
||||
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address box sizing set to content-box in IE 8/9.
|
||||
* 2. Remove excess padding in IE 8/9.
|
||||
* 3. Remove excess padding in IE 7.
|
||||
* Known issue: excess padding remains in IE 6.
|
||||
*/
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
*height: 13px; /* 3 */
|
||||
*width: 13px; /* 3 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
|
||||
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
|
||||
* (include `-moz` to future-proof).
|
||||
*/
|
||||
|
||||
input[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box; /* 2 */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and search cancel button in Safari 5 and Chrome
|
||||
* on OS X.
|
||||
*/
|
||||
|
||||
input[type="search"]::-webkit-search-cancel-button,
|
||||
input[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove inner padding and border in Firefox 3+.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove default vertical scrollbar in IE 6/7/8/9.
|
||||
* 2. Improve readability and alignment in all browsers.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto; /* 1 */
|
||||
vertical-align: top; /* 2 */
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
Tables
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove most spacing between table cells.
|
||||
*/
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
80
src/main/resources/static/style/theme.css
Normal file
80
src/main/resources/static/style/theme.css
Normal file
@@ -0,0 +1,80 @@
|
||||
body{
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.navbar.navbar-app {
|
||||
background-color: #F26E5F;
|
||||
color: rgba(255,255,255, 0.84);
|
||||
}
|
||||
|
||||
.foot-wrap{
|
||||
background-color: #5B90E7;
|
||||
height:100px;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.foot-wrap > *{
|
||||
margin-top:20px;
|
||||
}
|
||||
|
||||
.avatar{
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px){
|
||||
.left{
|
||||
/*background-color: #5B90E7;*/
|
||||
/*color: #FFFFFF;*/
|
||||
/*margin-top:-20px;*/
|
||||
margin-left: 20px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.left .separator{
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
content: "";
|
||||
}
|
||||
.left .nav {
|
||||
font-size: 20px;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.right{
|
||||
width: 61.8%;
|
||||
margin-left: 23.2%;
|
||||
} }
|
||||
.right{
|
||||
|
||||
}
|
||||
|
||||
.right .separator{
|
||||
/*clear: both;*/
|
||||
overflow: hidden;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
content: "";
|
||||
}
|
||||
|
||||
a, a:hover, a:focus {
|
||||
color: #000000;
|
||||
}
|
||||
a {
|
||||
color: #337ab7;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.form-group .checkbox label, .form-group .radio label, .form-group label{
|
||||
color:#333333;
|
||||
}
|
||||
|
||||
.form-group.is-focused .radio label:hover, .form-group.is-focused label.radio-inline:hover, .form-group.is-focused .radio label:focus, .form-group.is-focused label.radio-inline:focus{
|
||||
color:#333333;
|
||||
}
|
||||
126
src/main/resources/static/style/timeline.css
Normal file
126
src/main/resources/static/style/timeline.css
Normal file
@@ -0,0 +1,126 @@
|
||||
.timeline {
|
||||
list-style: none;
|
||||
padding: 20px 0 20px;
|
||||
position: relative;
|
||||
}
|
||||
.timeline:before {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
content: " ";
|
||||
width: 3px;
|
||||
background-color: #eeeeee;
|
||||
left: 50%;
|
||||
margin-left: -1.5px;
|
||||
}
|
||||
.timeline > li {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
.timeline > li:before,
|
||||
.timeline > li:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.timeline > li:after {
|
||||
clear: both;
|
||||
}
|
||||
.timeline > li:before,
|
||||
.timeline > li:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.timeline > li:after {
|
||||
clear: both;
|
||||
}
|
||||
.timeline > li > .timeline-panel {
|
||||
width: 46%;
|
||||
float: left;
|
||||
border: 1px solid #d4d4d4;
|
||||
border-radius: 2px;
|
||||
padding: 20px;
|
||||
position: relative;
|
||||
-webkit-box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.175);
|
||||
}
|
||||
.timeline > li > .timeline-panel:before {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
right: -15px;
|
||||
display: inline-block;
|
||||
border-top: 15px solid transparent;
|
||||
border-left: 15px solid #ccc;
|
||||
border-right: 0 solid #ccc;
|
||||
border-bottom: 15px solid transparent;
|
||||
content: " ";
|
||||
}
|
||||
.timeline > li > .timeline-panel:after {
|
||||
position: absolute;
|
||||
top: 27px;
|
||||
right: -14px;
|
||||
display: inline-block;
|
||||
border-top: 14px solid transparent;
|
||||
border-left: 14px solid #fff;
|
||||
border-right: 0 solid #fff;
|
||||
border-bottom: 14px solid transparent;
|
||||
content: " ";
|
||||
}
|
||||
.timeline > li > .timeline-badge {
|
||||
color: #fff;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 1.4em;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 50%;
|
||||
margin-left: -25px;
|
||||
background-color: #999999;
|
||||
z-index: 100;
|
||||
border-top-right-radius: 50%;
|
||||
border-top-left-radius: 50%;
|
||||
border-bottom-right-radius: 50%;
|
||||
border-bottom-left-radius: 50%;
|
||||
}
|
||||
.timeline > li.timeline-inverted > .timeline-panel {
|
||||
float: right;
|
||||
}
|
||||
.timeline > li.timeline-inverted > .timeline-panel:before {
|
||||
border-left-width: 0;
|
||||
border-right-width: 15px;
|
||||
left: -15px;
|
||||
right: auto;
|
||||
}
|
||||
.timeline > li.timeline-inverted > .timeline-panel:after {
|
||||
border-left-width: 0;
|
||||
border-right-width: 14px;
|
||||
left: -14px;
|
||||
right: auto;
|
||||
}
|
||||
.timeline-badge.primary {
|
||||
background-color: #2e6da4 !important;
|
||||
}
|
||||
.timeline-badge.success {
|
||||
background-color: #3f903f !important;
|
||||
}
|
||||
.timeline-badge.warning {
|
||||
background-color: #f0ad4e !important;
|
||||
}
|
||||
.timeline-badge.danger {
|
||||
background-color: #d9534f !important;
|
||||
}
|
||||
.timeline-badge.info {
|
||||
background-color: #5bc0de !important;
|
||||
}
|
||||
.timeline-title {
|
||||
margin-top: 0;
|
||||
color: inherit;
|
||||
}
|
||||
.timeline-body > p,
|
||||
.timeline-body > ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.timeline-body > p + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
56
src/main/resources/static/vendor/angular-material/angular-animate.min.js
vendored
Normal file
56
src/main/resources/static/vendor/angular-material/angular-animate.min.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
AngularJS v1.5.3
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(E,w,Va){'use strict';function ya(a,b,c){if(!a)throw Ka("areq",b||"?",c||"required");return a}function za(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ba(a)&&(a=a.join(" "));ba(b)&&(b=b.join(" "));return a+" "+b}function La(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function V(a,b,c){var d="";a=ba(a)?a:a&&I(a)&&a.length?a.split(/\s+/):[];q(a,function(a,f){a&&0<a.length&&(d+=0<f?" ":"",d+=c?b+a:a+b)});return d}function Ma(a){if(a instanceof H)switch(a.length){case 0:return[];
|
||||
case 1:if(1===a[0].nodeType)return a;break;default:return H(ga(a))}if(1===a.nodeType)return H(a)}function ga(a){if(!a[0])return a;for(var b=0;b<a.length;b++){var c=a[b];if(1==c.nodeType)return c}}function Na(a,b,c){q(b,function(b){a.addClass(b,c)})}function Oa(a,b,c){q(b,function(b){a.removeClass(b,c)})}function Q(a){return function(b,c){c.addClass&&(Na(a,b,c.addClass),c.addClass=null);c.removeClass&&(Oa(a,b,c.removeClass),c.removeClass=null)}}function oa(a){a=a||{};if(!a.$$prepared){var b=a.domOperation||
|
||||
O;a.domOperation=function(){a.$$domOperationFired=!0;b();b=O};a.$$prepared=!0}return a}function ha(a,b){Aa(a,b);Ba(a,b)}function Aa(a,b){b.from&&(a.css(b.from),b.from=null)}function Ba(a,b){b.to&&(a.css(b.to),b.to=null)}function T(a,b,c){var d=b.options||{};c=c.options||{};var e=(d.addClass||"")+" "+(c.addClass||""),f=(d.removeClass||"")+" "+(c.removeClass||"");a=Pa(a.attr("class"),e,f);c.preparationClasses&&(d.preparationClasses=W(c.preparationClasses,d.preparationClasses),delete c.preparationClasses);
|
||||
e=d.domOperation!==O?d.domOperation:null;Ca(d,c);e&&(d.domOperation=e);d.addClass=a.addClass?a.addClass:null;d.removeClass=a.removeClass?a.removeClass:null;b.addClass=d.addClass;b.removeClass=d.removeClass;return d}function Pa(a,b,c){function d(a){I(a)&&(a=a.split(" "));var b={};q(a,function(a){a.length&&(b[a]=!0)});return b}var e={};a=d(a);b=d(b);q(b,function(a,b){e[b]=1});c=d(c);q(c,function(a,b){e[b]=1===e[b]?null:-1});var f={addClass:"",removeClass:""};q(e,function(b,c){var d,e;1===b?(d="addClass",
|
||||
e=!a[c]):-1===b&&(d="removeClass",e=a[c]);e&&(f[d].length&&(f[d]+=" "),f[d]+=c)});return f}function A(a){return a instanceof w.element?a[0]:a}function Qa(a,b,c){var d="";b&&(d=V(b,"ng-",!0));c.addClass&&(d=W(d,V(c.addClass,"-add")));c.removeClass&&(d=W(d,V(c.removeClass,"-remove")));d.length&&(c.preparationClasses=d,a.addClass(d))}function pa(a,b){var c=b?"-"+b+"s":"";la(a,[ma,c]);return[ma,c]}function ra(a,b){var c=b?"paused":"",d=X+"PlayState";la(a,[d,c]);return[d,c]}function la(a,b){a.style[b[0]]=
|
||||
b[1]}function W(a,b){return a?b?a+" "+b:a:b}function Da(a,b,c){var d=Object.create(null),e=a.getComputedStyle(b)||{};q(c,function(a,b){var c=e[a];if(c){var F=c.charAt(0);if("-"===F||"+"===F||0<=F)c=Ra(c);0===c&&(c=null);d[b]=c}});return d}function Ra(a){var b=0;a=a.split(/\s*,\s*/);q(a,function(a){"s"==a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function sa(a){return 0===a||null!=a}function Ea(a,b){var c=R,d=a+"s";b?c+="Duration":d+=" linear all";
|
||||
return[c,d]}function Fa(){var a=Object.create(null);return{flush:function(){a=Object.create(null)},count:function(b){return(b=a[b])?b.total:0},get:function(b){return(b=a[b])&&b.value},put:function(b,c){a[b]?a[b].total++:a[b]={total:1,value:c}}}}function Ga(a,b,c){q(c,function(c){a[c]=Y(a[c])?a[c]:b.style.getPropertyValue(c)})}var O=w.noop,Ha=w.copy,Ca=w.extend,H=w.element,q=w.forEach,ba=w.isArray,I=w.isString,ca=w.isObject,N=w.isUndefined,Y=w.isDefined,Ia=w.isFunction,ta=w.isElement,R,ua,X,va;N(E.ontransitionend)&&
|
||||
Y(E.onwebkittransitionend)?(R="WebkitTransition",ua="webkitTransitionEnd transitionend"):(R="transition",ua="transitionend");N(E.onanimationend)&&Y(E.onwebkitanimationend)?(X="WebkitAnimation",va="webkitAnimationEnd animationend"):(X="animation",va="animationend");var qa=X+"Delay",wa=X+"Duration",ma=R+"Delay";E=R+"Duration";var Ka=w.$$minErr("ng"),Sa={transitionDuration:E,transitionDelay:ma,transitionProperty:R+"Property",animationDuration:wa,animationDelay:qa,animationIterationCount:X+"IterationCount"},
|
||||
Ta={transitionDuration:E,transitionDelay:ma,animationDuration:wa,animationDelay:qa};w.module("ngAnimate",[]).directive("ngAnimateSwap",["$animate","$rootScope",function(a,b){return{restrict:"A",transclude:"element",terminal:!0,priority:600,link:function(b,d,e,f,r){var x,F;b.$watchCollection(e.ngAnimateSwap||e["for"],function(e){x&&a.leave(x);F&&(F.$destroy(),F=null);if(e||0===e)F=b.$new(),r(F,function(b){x=b;a.enter(b,null,d)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,
|
||||
c,d){function e(a){c.data("$$ngAnimateChildren","on"===a||"true"===a)}var f=d.ngAnimateChildren;w.isString(f)&&0===f.length?c.data("$$ngAnimateChildren",!0):(e(a(f)(b)),d.$observe("ngAnimateChildren",e))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),r=0;r<b.length;r++)b[r]();e||a(function(){e||c()})}}var d,e;d=b.queue=[];b.waitUntilQuiet=function(b){e&&e();e=a(function(){e=null;b();c()})};return b}]).provider("$$animateQueue",
|
||||
["$animateProvider",function(a){function b(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);q(a,function(a){b[a]=!0});return b}function c(a,c){if(a&&c){var d=b(c);return a.split(" ").some(function(a){return d[a]})}}function d(a,b,c,d){return f[a].some(function(a){return a(b,c,d)})}function e(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var f=this.rules={skip:[],cancel:[],join:[]};f.join.push(function(a,b,c){return!b.structural&&e(b)});f.skip.push(function(a,
|
||||
b,c){return!b.structural&&!e(b)});f.skip.push(function(a,b,c){return"leave"==c.event&&b.structural});f.skip.push(function(a,b,c){return c.structural&&2===c.state&&!b.structural});f.cancel.push(function(a,b,c){return c.structural&&b.structural});f.cancel.push(function(a,b,c){return 2===c.state&&b.structural});f.cancel.push(function(a,b,d){if(d.structural)return!1;a=b.addClass;b=b.removeClass;var e=d.addClass;d=d.removeClass;return N(a)&&N(b)||N(e)&&N(d)?!1:c(a,d)||c(b,e)});this.$get=["$$rAF","$rootScope",
|
||||
"$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(b,c,f,u,l,w,s,M,v,h){function P(){var a=!1;return function(b){a?b():c.$$postDigest(function(){a=!0;b()})}}function y(a,b,c){var g=A(b),d=A(a),k=[];(a=G[c])&&q(a,function(a){t.call(a.node,g)?k.push(a.callback):"leave"===c&&t.call(a.node,d)&&k.push(a.callback)});return k}function k(a,g,k){function m(c,g,d,k){F(function(){var c=y(J,a,g);c.length&&b(function(){q(c,function(b){b(a,
|
||||
d,k)})})});c.progress(g,d,k)}function G(b){var c=a,g=h;g.preparationClasses&&(c.removeClass(g.preparationClasses),g.preparationClasses=null);g.activeClasses&&(c.removeClass(g.activeClasses),g.activeClasses=null);Ja(a,h);ha(a,h);h.domOperation();f.complete(!b)}var h=Ha(k),t,J;if(a=Ma(a))t=A(a),J=a.parent();var h=oa(h),f=new s,F=P();ba(h.addClass)&&(h.addClass=h.addClass.join(" "));h.addClass&&!I(h.addClass)&&(h.addClass=null);ba(h.removeClass)&&(h.removeClass=h.removeClass.join(" "));h.removeClass&&
|
||||
!I(h.removeClass)&&(h.removeClass=null);h.from&&!ca(h.from)&&(h.from=null);h.to&&!ca(h.to)&&(h.to=null);if(!t)return G(),f;k=[t.className,h.addClass,h.removeClass].join(" ");if(!Ua(k))return G(),f;var v=0<=["enter","move","leave"].indexOf(g),l=!K||u[0].hidden||C.get(t);k=!l&&z.get(t)||{};var Z=!!k.state;l||Z&&1==k.state||(l=!n(a,J,g));if(l)return G(),f;v&&xa(a);l={structural:v,element:a,event:g,addClass:h.addClass,removeClass:h.removeClass,close:G,options:h,runner:f};if(Z){if(d("skip",a,l,k)){if(2===
|
||||
k.state)return G(),f;T(a,k,l);return k.runner}if(d("cancel",a,l,k))if(2===k.state)k.runner.end();else if(k.structural)k.close();else return T(a,k,l),k.runner;else if(d("join",a,l,k))if(2===k.state)T(a,l,{});else return Qa(a,v?g:null,h),g=l.event=k.event,h=T(a,k,l),k.runner}else T(a,l,{});(Z=l.structural)||(Z="animate"===l.event&&0<Object.keys(l.options.to||{}).length||e(l));if(!Z)return G(),ka(a),f;var M=(k.counter||0)+1;l.counter=M;D(a,1,l);c.$$postDigest(function(){var b=z.get(t),c=!b,b=b||{},d=
|
||||
0<(a.parent()||[]).length&&("animate"===b.event||b.structural||e(b));if(c||b.counter!==M||!d){c&&(Ja(a,h),ha(a,h));if(c||v&&b.event!==g)h.domOperation(),f.end();d||ka(a)}else g=!b.structural&&e(b,!0)?"setClass":b.event,D(a,2),b=w(a,g,b.options),b.done(function(b){G(!b);(b=z.get(t))&&b.counter===M&&ka(A(a));m(f,g,"close",{})}),f.setHost(b),m(f,g,"start",{})});return f}function xa(a){a=A(a).querySelectorAll("[data-ng-animate]");q(a,function(a){var b=parseInt(a.getAttribute("data-ng-animate")),c=z.get(a);
|
||||
if(c)switch(b){case 2:c.runner.end();case 1:z.remove(a)}})}function ka(a){a=A(a);a.removeAttribute("data-ng-animate");z.remove(a)}function J(a,b){return A(a)===A(b)}function n(a,b,c){c=H(u[0].body);var g=J(a,c)||"HTML"===a[0].nodeName,d=J(a,f),k=!1,h,m=C.get(A(a));(a=H.data(a[0],"$ngAnimatePin"))&&(b=a);for(b=A(b);b;){d||(d=J(b,f));if(1!==b.nodeType)break;a=z.get(b)||{};if(!k){var e=C.get(b);if(!0===e&&!1!==m){m=!0;break}else!1===e&&(m=!1);k=a.structural}if(N(h)||!0===h)a=H.data(b,"$$ngAnimateChildren"),
|
||||
Y(a)&&(h=a);if(k&&!1===h)break;g||(g=J(b,c));if(g&&d)break;if(!d&&(a=H.data(b,"$ngAnimatePin"))){b=A(a);continue}b=b.parentNode}return(!k||h)&&!0!==m&&d&&g}function D(a,b,c){c=c||{};c.state=b;a=A(a);a.setAttribute("data-ng-animate",b);c=(b=z.get(a))?Ca(b,c):c;z.put(a,c)}var z=new l,C=new l,K=null,g=c.$watch(function(){return 0===M.totalPendingRequests},function(a){a&&(g(),c.$$postDigest(function(){c.$$postDigest(function(){null===K&&(K=!0)})}))}),G={},m=a.classNameFilter(),Ua=m?function(a){return m.test(a)}:
|
||||
function(){return!0},Ja=Q(v),t=Node.prototype.contains||function(a){return this===a||!!(this.compareDocumentPosition(a)&16)},Z={on:function(a,b,c){var g=ga(b);G[a]=G[a]||[];G[a].push({node:g,callback:c});H(b).on("$destroy",function(){Z.off(a,b,c)})},off:function(a,b,c){function g(a,b,c){var d=ga(b);return a.filter(function(a){return!(a.node===d&&(!c||a.callback===c))})}var d=G[a];d&&(G[a]=1===arguments.length?null:g(d,b,c))},pin:function(a,b){ya(ta(a),"element","not an element");ya(ta(b),"parentElement",
|
||||
"not an element");a.data("$ngAnimatePin",b)},push:function(a,b,c,g){c=c||{};c.domOperation=g;return k(a,b,c)},enabled:function(a,b){var c=arguments.length;if(0===c)b=!!K;else if(ta(a)){var g=A(a),d=C.get(g);1===c?b=!d:C.put(g,!b)}else b=K=!!a;return b}};return Z}]}]).provider("$$animation",["$animateProvider",function(a){function b(a){return a.data("$$animationRunner")}var c=this.drivers=[];this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(a,e,
|
||||
f,r,x,F){function u(a){function b(a){if(a.processed)return a;a.processed=!0;var d=a.domNode,h=d.parentNode;e.put(d,a);for(var f;h;){if(f=e.get(h)){f.processed||(f=b(f));break}h=h.parentNode}(f||c).children.push(a);return a}var c={children:[]},d,e=new x;for(d=0;d<a.length;d++){var f=a[d];e.put(f.domNode,a[d]={domNode:f.domNode,fn:f.fn,children:[]})}for(d=0;d<a.length;d++)b(a[d]);return function(a){var b=[],c=[],d;for(d=0;d<a.children.length;d++)c.push(a.children[d]);a=c.length;var h=0,e=[];for(d=0;d<
|
||||
c.length;d++){var f=c[d];0>=a&&(a=h,h=0,b.push(e),e=[]);e.push(f.fn);f.children.forEach(function(a){h++;c.push(a)});a--}e.length&&b.push(e);return b}(c)}var l=[],w=Q(a);return function(s,x,v){function h(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];q(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&b.push(a)});return b}function P(a){var b=[],c={};q(a,function(a,g){var d=A(a.element),k=0<=["enter","move"].indexOf(a.event),d=a.structural?
|
||||
h(d):[];if(d.length){var e=k?"to":"from";q(d,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][e]={animationID:g,element:H(a)}})}else b.push(a)});var d={},k={};q(c,function(c,h){var e=c.from,f=c.to;if(e&&f){var m=a[e.animationID],C=a[f.animationID],n=e.animationID.toString();if(!k[n]){var D=k[n]={structural:!0,beforeStart:function(){m.beforeStart();C.beforeStart()},close:function(){m.close();C.close()},classes:y(m.classes,C.classes),from:m,to:C,anchors:[]};D.classes.length?b.push(D):
|
||||
(b.push(m),b.push(C))}k[n].anchors.push({out:e.element,"in":f.element})}else e=e?e.animationID:f.animationID,f=e.toString(),d[f]||(d[f]=!0,b.push(a[e]))});return b}function y(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d<a.length;d++){var k=a[d];if("ng-"!==k.substring(0,3))for(var e=0;e<b.length;e++)if(k===b[e]){c.push(k);break}}return c.join(" ")}function k(a){for(var b=c.length-1;0<=b;b--){var d=c[b];if(f.has(d)&&(d=f.get(d)(a)))return d}}function xa(a,c){a.from&&a.to?(b(a.from.element).setHost(c),
|
||||
b(a.to.element).setHost(c)):b(a.element).setHost(c)}function ka(){var a=b(s);!a||"leave"===x&&v.$$domOperationFired||a.end()}function J(b){s.off("$destroy",ka);s.removeData("$$animationRunner");w(s,v);ha(s,v);v.domOperation();C&&a.removeClass(s,C);s.removeClass("ng-animate");D.complete(!b)}v=oa(v);var n=0<=["enter","move","leave"].indexOf(x),D=new r({end:function(){J()},cancel:function(){J(!0)}});if(!c.length)return J(),D;s.data("$$animationRunner",D);var z=za(s.attr("class"),za(v.addClass,v.removeClass)),
|
||||
C=v.tempClasses;C&&(z+=" "+C,v.tempClasses=null);var K;n&&(K="ng-"+x+"-prepare",a.addClass(s,K));l.push({element:s,classes:z,event:x,structural:n,options:v,beforeStart:function(){s.addClass("ng-animate");C&&a.addClass(s,C);K&&(a.removeClass(s,K),K=null)},close:J});s.on("$destroy",ka);if(1<l.length)return D;e.$$postDigest(function(){var a=[];q(l,function(c){b(c.element)?a.push(c):c.close()});l.length=0;var c=P(a),d=[];q(c,function(a){d.push({domNode:A(a.from?a.from.element:a.element),fn:function(){a.beforeStart();
|
||||
var c,d=a.close;if(b(a.anchors?a.from.element||a.to.element:a.element)){var g=k(a);g&&(c=g.start)}c?(c=c(),c.done(function(a){d(!a)}),xa(a,c)):d()}})});F(u(d))});return D}}]}]).provider("$animateCss",["$animateProvider",function(a){var b=Fa(),c=Fa();this.$get=["$window","$$jqLite","$$AnimateRunner","$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(a,e,f,r,x,F,u,l){function w(a,b){var c=a.parentNode;return(c.$$ngAnimateParentKey||(c.$$ngAnimateParentKey=++P))+"-"+a.getAttribute("class")+
|
||||
"-"+b}function s(k,h,f,l){var n;0<b.count(f)&&(n=c.get(f),n||(h=V(h,"-stagger"),e.addClass(k,h),n=Da(a,k,l),n.animationDuration=Math.max(n.animationDuration,0),n.transitionDuration=Math.max(n.transitionDuration,0),e.removeClass(k,h),c.put(f,n)));return n||{}}function M(a){y.push(a);u.waitUntilQuiet(function(){b.flush();c.flush();for(var a=x(),d=0;d<y.length;d++)y[d](a);y.length=0})}function v(c,h,e){h=b.get(e);h||(h=Da(a,c,Sa),"infinite"===h.animationIterationCount&&(h.animationIterationCount=1));
|
||||
b.put(e,h);c=h;e=c.animationDelay;h=c.transitionDelay;c.maxDelay=e&&h?Math.max(e,h):e||h;c.maxDuration=Math.max(c.animationDuration*c.animationIterationCount,c.transitionDuration);return c}var h=Q(e),P=0,y=[];return function(a,c){function d(){n()}function u(){n(!0)}function n(b){if(!(P||H&&da)){P=!0;da=!1;g.$$skipPreparationClasses||e.removeClass(a,fa);e.removeClass(a,ga);ra(m,!1);pa(m,!1);q(y,function(a){m.style[a[0]]=""});h(a,g);ha(a,g);Object.keys(G).length&&q(G,function(a,b){a?m.style.setProperty(b,
|
||||
a):m.style.removeProperty(b)});if(g.onDone)g.onDone();ea&&ea.length&&a.off(ea.join(" "),C);var c=a.data("$$animateCss");c&&(r.cancel(c[0].timer),a.removeData("$$animateCss"));E&&E.complete(!b)}}function D(a){p.blockTransition&&pa(m,a);p.blockKeyframeAnimation&&ra(m,!!a)}function z(){E=new f({end:d,cancel:u});M(O);n();return{$$willAnimate:!1,start:function(){return E},end:d}}function C(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));
|
||||
Math.max(a-W,0)>=Q&&b>=L&&(H=!0,n())}function K(){function b(){if(!P){D(!1);q(y,function(a){m.style[a[0]]=a[1]});h(a,g);e.addClass(a,ga);if(p.recalculateTimingStyles){na=m.className+" "+fa;ia=w(m,na);B=v(m,na,ia);$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;if(0===L){n();return}p.hasTransitions=0<B.transitionDuration;p.hasAnimations=0<B.animationDuration}p.applyAnimationDelay&&($="boolean"!==typeof g.delay&&sa(g.delay)?parseFloat(g.delay):$,I=Math.max($,0),B.animationDelay=$,aa=[qa,$+"s"],y.push(aa),
|
||||
m.style[aa[0]]=aa[1]);Q=1E3*I;T=1E3*L;if(g.easing){var d,f=g.easing;p.hasTransitions&&(d=R+"TimingFunction",y.push([d,f]),m.style[d]=f);p.hasAnimations&&(d=X+"TimingFunction",y.push([d,f]),m.style[d]=f)}B.transitionDuration&&ea.push(ua);B.animationDuration&&ea.push(va);W=Date.now();var l=Q+1.5*T;d=W+l;var f=a.data("$$animateCss")||[],K=!0;if(f.length){var z=f[0];(K=d>z.expectedEndTime)?r.cancel(z.timer):f.push(n)}K&&(l=r(c,l,!1),f[0]={timer:l,expectedEndTime:d},f.push(n),a.data("$$animateCss",f));
|
||||
if(ea.length)a.on(ea.join(" "),C);g.to&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.to)),Ba(a,g))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d<b.length;d++)b[d]();a.removeData("$$animateCss")}}if(!P)if(m.parentNode){var d=function(a){if(H)da&&a&&(da=!1,n());else if(da=!a,B.animationDuration)if(a=ra(m,da),da)y.push(a);else{var b=y,c=b.indexOf(a);0<=a&&b.splice(c,1)}},f=0<ca&&(B.transitionDuration&&0===U.transitionDuration||B.animationDuration&&0===U.animationDuration)&&Math.max(U.animationDelay,
|
||||
U.transitionDelay);f?r(b,Math.floor(f*ca*1E3),!1):b();N.resume=function(){d(!0)};N.pause=function(){d(!1)}}else n()}var g=c||{};g.$$prepared||(g=oa(Ha(g)));var G={},m=A(a);if(!m||!m.parentNode||!l.enabled())return z();var y=[],x=a.attr("class"),t=La(g),P,da,H,E,N,I,Q,L,T,W,ea=[];if(0===g.duration||!F.animations&&!F.transitions)return z();var ja=g.event&&ba(g.event)?g.event.join(" "):g.event,Y="",S="";ja&&g.structural?Y=V(ja,"ng-",!0):ja&&(Y=ja);g.addClass&&(S+=V(g.addClass,"-add"));g.removeClass&&
|
||||
(S.length&&(S+=" "),S+=V(g.removeClass,"-remove"));g.applyClassesEarly&&S.length&&h(a,g);var fa=[Y,S].join(" ").trim(),na=x+" "+fa,ga=V(fa,"-active"),x=t.to&&0<Object.keys(t.to).length;if(!(0<(g.keyframeStyle||"").length||x||fa))return z();var ia,U;0<g.stagger?(t=parseFloat(g.stagger),U={transitionDelay:t,animationDelay:t,transitionDuration:0,animationDuration:0}):(ia=w(m,na),U=s(m,fa,ia,Ta));g.$$skipPreparationClasses||e.addClass(a,fa);g.transitionStyle&&(t=[R,g.transitionStyle],la(m,t),y.push(t));
|
||||
0<=g.duration&&(t=0<m.style[R].length,t=Ea(g.duration,t),la(m,t),y.push(t));g.keyframeStyle&&(t=[X,g.keyframeStyle],la(m,t),y.push(t));var ca=U?0<=g.staggerIndex?g.staggerIndex:b.count(ia):0;(ja=0===ca)&&!g.skipBlocking&&pa(m,9999);var B=v(m,na,ia),$=B.maxDelay;I=Math.max($,0);L=B.maxDuration;var p={};p.hasTransitions=0<B.transitionDuration;p.hasAnimations=0<B.animationDuration;p.hasTransitionAll=p.hasTransitions&&"all"==B.transitionProperty;p.applyTransitionDuration=x&&(p.hasTransitions&&!p.hasTransitionAll||
|
||||
p.hasAnimations&&!p.hasTransitions);p.applyAnimationDuration=g.duration&&p.hasAnimations;p.applyTransitionDelay=sa(g.delay)&&(p.applyTransitionDuration||p.hasTransitions);p.applyAnimationDelay=sa(g.delay)&&p.hasAnimations;p.recalculateTimingStyles=0<S.length;if(p.applyTransitionDuration||p.applyAnimationDuration)L=g.duration?parseFloat(g.duration):L,p.applyTransitionDuration&&(p.hasTransitions=!0,B.transitionDuration=L,t=0<m.style[R+"Property"].length,y.push(Ea(L,t))),p.applyAnimationDuration&&(p.hasAnimations=
|
||||
!0,B.animationDuration=L,y.push([wa,L+"s"]));if(0===L&&!p.recalculateTimingStyles)return z();if(null!=g.delay){var aa;"boolean"!==typeof g.delay&&(aa=parseFloat(g.delay),I=Math.max(aa,0));p.applyTransitionDelay&&y.push([ma,aa+"s"]);p.applyAnimationDelay&&y.push([qa,aa+"s"])}null==g.duration&&0<B.transitionDuration&&(p.recalculateTimingStyles=p.recalculateTimingStyles||ja);Q=1E3*I;T=1E3*L;g.skipBlocking||(p.blockTransition=0<B.transitionDuration,p.blockKeyframeAnimation=0<B.animationDuration&&0<U.animationDelay&&
|
||||
0===U.animationDuration);g.from&&(g.cleanupStyles&&Ga(G,m,Object.keys(g.from)),Aa(a,g));p.blockTransition||p.blockKeyframeAnimation?D(L):g.skipBlocking||pa(m,!1);return{$$willAnimate:!0,end:d,start:function(){if(!P)return N={end:d,cancel:u,resume:null,pause:null},E=new f(N),M(K),E}}}}]}]).provider("$$animateCssDriver",["$$animationProvider",function(a){a.drivers.push("$$animateCssDriver");this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(a,
|
||||
c,d,e,f,r,x){function F(a){return a.replace(/\bng-\S+\b/g,"")}function u(a,b){I(a)&&(a=a.split(" "));I(b)&&(b=b.split(" "));return a.filter(function(a){return-1===b.indexOf(a)}).join(" ")}function l(c,e,f){function k(a){var b={},c=A(a).getBoundingClientRect();q(["width","height","top","left"],function(a){var d=c[a];switch(a){case "top":d+=M.scrollTop;break;case "left":d+=M.scrollLeft}b[a]=Math.floor(d)+"px"});return b}function l(){var c=F(f.attr("class")||""),d=u(c,n),c=u(n,c),d=a(r,{to:k(f),addClass:"ng-anchor-in "+
|
||||
d,removeClass:"ng-anchor-out "+c,delay:!0});return d.$$willAnimate?d:null}function x(){r.remove();e.removeClass("ng-animate-shim");f.removeClass("ng-animate-shim")}var r=H(A(e).cloneNode(!0)),n=F(r.attr("class")||"");e.addClass("ng-animate-shim");f.addClass("ng-animate-shim");r.addClass("ng-anchor");v.append(r);var D;c=function(){var c=a(r,{addClass:"ng-anchor-out",delay:!0,from:k(e)});return c.$$willAnimate?c:null}();if(!c&&(D=l(),!D))return x();var z=c||D;return{start:function(){function a(){c&&
|
||||
c.end()}var b,c=z.start();c.done(function(){c=null;if(!D&&(D=l()))return c=D.start(),c.done(function(){c=null;x();b.complete()}),c;x();b.complete()});return b=new d({end:a,cancel:a})}}}function w(a,b,c,e){var f=s(a,O),r=s(b,O),x=[];q(e,function(a){(a=l(c,a.out,a["in"]))&&x.push(a)});if(f||r||0!==x.length)return{start:function(){function a(){q(b,function(a){a.end()})}var b=[];f&&b.push(f.start());r&&b.push(r.start());q(x,function(a){b.push(a.start())});var c=new d({end:a,cancel:a});d.all(b,function(a){c.complete(a)});
|
||||
return c}}}function s(c){var d=c.element,e=c.options||{};c.structural&&(e.event=c.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===c.event&&(e.onDone=e.domOperation));e.preparationClasses&&(e.event=W(e.event,e.preparationClasses));c=a(d,e);return c.$$willAnimate?c:null}if(!f.animations&&!f.transitions)return O;var M=x[0].body;c=A(e);var v=H(c.parentNode&&11===c.parentNode.nodeType||M.contains(c)?c:M);Q(r);return function(a){return a.from&&a.to?w(a.from,a.to,a.classes,a.anchors):s(a)}}]}]).provider("$$animateJs",
|
||||
["$animateProvider",function(a){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(b,c,d){function e(c){c=ba(c)?c:c.split(" ");for(var d=[],e={},f=0;f<c.length;f++){var l=c[f],q=a.$$registeredAnimations[l];q&&!e[l]&&(d.push(b.get(q)),e[l]=!0)}return d}var f=Q(d);return function(a,b,d,u){function l(){u.domOperation();f(a,u)}function w(a,b,d,e,g){switch(d){case "animate":b=[b,e.from,e.to,g];break;case "setClass":b=[b,h,H,g];break;case "addClass":b=[b,h,g];break;case "removeClass":b=[b,H,g];
|
||||
break;default:b=[b,g]}b.push(e);if(a=a.apply(a,b))if(Ia(a.start)&&(a=a.start()),a instanceof c)a.done(g);else if(Ia(a))return a;return O}function s(a,b,d,e,g){var f=[];q(e,function(e){var h=e[g];h&&f.push(function(){var e,g,f=!1,k=function(a){f||(f=!0,(g||O)(a),e.complete(!a))};e=new c({end:function(){k()},cancel:function(){k(!0)}});g=w(h,a,b,d,function(a){k(!1===a)});return e})});return f}function A(a,b,d,e,g){var f=s(a,b,d,e,g);if(0===f.length){var h,k;"beforeSetClass"===g?(h=s(a,"removeClass",
|
||||
d,e,"beforeRemoveClass"),k=s(a,"addClass",d,e,"beforeAddClass")):"setClass"===g&&(h=s(a,"removeClass",d,e,"removeClass"),k=s(a,"addClass",d,e,"addClass"));h&&(f=f.concat(h));k&&(f=f.concat(k))}if(0!==f.length)return function(a){var b=[];f.length&&q(f,function(a){b.push(a())});b.length?c.all(b,a):a();return function(a){q(b,function(b){a?b.cancel():b.end()})}}}var v=!1;3===arguments.length&&ca(d)&&(u=d,d=null);u=oa(u);d||(d=a.attr("class")||"",u.addClass&&(d+=" "+u.addClass),u.removeClass&&(d+=" "+
|
||||
u.removeClass));var h=u.addClass,H=u.removeClass,y=e(d),k,E;if(y.length){var I,J;"leave"==b?(J="leave",I="afterLeave"):(J="before"+b.charAt(0).toUpperCase()+b.substr(1),I=b);"enter"!==b&&"move"!==b&&(k=A(a,b,u,y,J));E=A(a,b,u,y,I)}if(k||E){var n;return{$$willAnimate:!0,end:function(){n?n.end():(v=!0,l(),ha(a,u),n=new c,n.complete(!0));return n},start:function(){function b(c){v=!0;l();ha(a,u);n.complete(c)}if(n)return n;n=new c;var d,e=[];k&&e.push(function(a){d=k(a)});e.length?e.push(function(a){l();
|
||||
a(!0)}):l();E&&e.push(function(a){d=E(a)});n.setHost({end:function(){v||((d||O)(void 0),b(void 0))},cancel:function(){v||((d||O)(!0),b(!0))}});c.chain(e,b);return n}}}}}]}]).provider("$$animateJsDriver",["$$animationProvider",function(a){a.drivers.push("$$animateJsDriver");this.$get=["$$animateJs","$$AnimateRunner",function(a,c){function d(c){return a(c.element,c.event,c.classes,c.options)}return function(a){if(a.from&&a.to){var b=d(a.from),r=d(a.to);if(b||r)return{start:function(){function a(){return function(){q(d,
|
||||
function(a){a.end()})}}var d=[];b&&d.push(b.start());r&&d.push(r.start());c.all(d,function(a){e.complete(a)});var e=new c({end:a(),cancel:a()});return e}}}else return d(a)}}]}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-animate.min.js.map
|
||||
14
src/main/resources/static/vendor/angular-material/angular-aria.min.js
vendored
Normal file
14
src/main/resources/static/vendor/angular-material/angular-aria.min.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
AngularJS v1.5.3
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(s,q,t){'use strict';var f="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};q.module("ngAria",["ng"]).provider("$aria",function(){function a(a,h,p,n){return function(d,e,b){var g=b.$normalize(h);!c[g]||l(e,p)||b[g]||d.$watch(b[a],function(b){b=n?!b:!!b;e.attr(h,b)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};this.config=
|
||||
function(a){c=q.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",f,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",f,!1)}]).directive("ngRequired",
|
||||
["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",f,!1)}]).directive("ngModel",["$aria",function(a){function c(c,n,d,e){return a.config(n)&&!d.attr(c)&&(e||!l(d,f))}function m(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function h(a,c){var d=a.type,e=a.role;return"checkbox"===(d||e)||"menuitemcheckbox"===e?"checkbox":"radio"===(d||e)||"menuitemradio"===e?"radio":"range"===d||"progressbar"===e||"slider"===e?"range":""}return{restrict:"A",require:"ngModel",
|
||||
priority:200,compile:function(f,n){var d=h(n,f);return{pre:function(a,b,c,k){"checkbox"===d&&(k.$isEmpty=function(a){return!1===a})},post:function(e,b,g,k){function f(){return k.$modelValue}function h(a){b.attr("aria-checked",g.value==k.$viewValue)}function n(){b.attr("aria-checked",!k.$isEmpty(k.$viewValue))}var l=c("tabindex","tabindex",b,!1);switch(d){case "radio":case "checkbox":m(d,b)&&b.attr("role",d);c("aria-checked","ariaChecked",b,!1)&&e.$watch(f,"radio"===d?h:n);l&&b.attr("tabindex",0);
|
||||
break;case "range":m(d,b)&&b.attr("role","slider");if(a.config("ariaValue")){var p=!b.attr("aria-valuemin")&&(g.hasOwnProperty("min")||g.hasOwnProperty("ngMin")),q=!b.attr("aria-valuemax")&&(g.hasOwnProperty("max")||g.hasOwnProperty("ngMax")),r=!b.attr("aria-valuenow");p&&g.$observe("min",function(a){b.attr("aria-valuemin",a)});q&&g.$observe("max",function(a){b.attr("aria-valuemax",a)});r&&e.$watch(f,function(a){b.attr("aria-valuenow",a)})}l&&b.attr("tabindex",0)}!g.hasOwnProperty("ngRequired")&&
|
||||
k.$validators.required&&c("aria-required","ariaRequired",b,!1)&&g.$observe("required",function(){b.attr("aria-required",!!g.required)});c("aria-invalid","ariaInvalid",b,!0)&&e.$watch(function(){return k.$invalid},function(a){b.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",f,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",link:function(a,c,f,h){c.attr("aria-live")||c.attr("aria-live",
|
||||
"assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(m,h){var p=c(h.ngClick,null,!0);return function(c,d,e){if(!l(d,f)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!e.ngKeypress))d.on("keypress",function(a){function d(){p(c,{$event:a})}var e=a.which||a.keyCode;32!==e&&13!==e||c.$apply(d)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,
|
||||
m,h){!a.config("tabindex")||m.attr("tabindex")||l(m,f)||m.attr("tabindex",0)}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-aria.min.js.map
|
||||
16862
src/main/resources/static/vendor/angular-material/angular-material.min.css
vendored
Normal file
16862
src/main/resources/static/vendor/angular-material/angular-material.min.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
src/main/resources/static/vendor/angular-material/angular-material.min.js
vendored
Normal file
15
src/main/resources/static/vendor/angular-material/angular-material.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4148
src/main/resources/static/vendor/angular/angular-animate.js
vendored
Normal file
4148
src/main/resources/static/vendor/angular/angular-animate.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
56
src/main/resources/static/vendor/angular/angular-animate.min.js
vendored
Normal file
56
src/main/resources/static/vendor/angular/angular-animate.min.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
AngularJS v1.5.6
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(S,B){'use strict';function Aa(a,b,c){if(!a)throw Ma("areq",b||"?",c||"required");return a}function Ba(a,b){if(!a&&!b)return"";if(!a)return b;if(!b)return a;ca(a)&&(a=a.join(" "));ca(b)&&(b=b.join(" "));return a+" "+b}function Na(a){var b={};a&&(a.to||a.from)&&(b.to=a.to,b.from=a.from);return b}function Y(a,b,c){var d="";a=ca(a)?a:a&&P(a)&&a.length?a.split(/\s+/):[];s(a,function(a,l){a&&0<a.length&&(d+=0<l?" ":"",d+=c?b+a:a+b)});return d}function Oa(a){if(a instanceof E)switch(a.length){case 0:return a;
|
||||
case 1:if(1===a[0].nodeType)return a;break;default:return E(da(a))}if(1===a.nodeType)return E(a)}function da(a){if(!a[0])return a;for(var b=0;b<a.length;b++){var c=a[b];if(1==c.nodeType)return c}}function Pa(a,b,c){s(b,function(b){a.addClass(b,c)})}function Qa(a,b,c){s(b,function(b){a.removeClass(b,c)})}function V(a){return function(b,c){c.addClass&&(Pa(a,b,c.addClass),c.addClass=null);c.removeClass&&(Qa(a,b,c.removeClass),c.removeClass=null)}}function pa(a){a=a||{};if(!a.$$prepared){var b=a.domOperation||
|
||||
R;a.domOperation=function(){a.$$domOperationFired=!0;b();b=R};a.$$prepared=!0}return a}function ha(a,b){Ca(a,b);Da(a,b)}function Ca(a,b){b.from&&(a.css(b.from),b.from=null)}function Da(a,b){b.to&&(a.css(b.to),b.to=null)}function W(a,b,c){var d=b.options||{};c=c.options||{};var e=(d.addClass||"")+" "+(c.addClass||""),l=(d.removeClass||"")+" "+(c.removeClass||"");a=Ra(a.attr("class"),e,l);c.preparationClasses&&(d.preparationClasses=Z(c.preparationClasses,d.preparationClasses),delete c.preparationClasses);
|
||||
e=d.domOperation!==R?d.domOperation:null;Ea(d,c);e&&(d.domOperation=e);d.addClass=a.addClass?a.addClass:null;d.removeClass=a.removeClass?a.removeClass:null;b.addClass=d.addClass;b.removeClass=d.removeClass;return d}function Ra(a,b,c){function d(a){P(a)&&(a=a.split(" "));var b={};s(a,function(a){a.length&&(b[a]=!0)});return b}var e={};a=d(a);b=d(b);s(b,function(a,b){e[b]=1});c=d(c);s(c,function(a,b){e[b]=1===e[b]?null:-1});var l={addClass:"",removeClass:""};s(e,function(b,c){var d,e;1===b?(d="addClass",
|
||||
e=!a[c]):-1===b&&(d="removeClass",e=a[c]);e&&(l[d].length&&(l[d]+=" "),l[d]+=c)});return l}function z(a){return a instanceof B.element?a[0]:a}function Sa(a,b,c){var d="";b&&(d=Y(b,"ng-",!0));c.addClass&&(d=Z(d,Y(c.addClass,"-add")));c.removeClass&&(d=Z(d,Y(c.removeClass,"-remove")));d.length&&(c.preparationClasses=d,a.addClass(d))}function qa(a,b){var c=b?"-"+b+"s":"";la(a,[ma,c]);return[ma,c]}function ta(a,b){var c=b?"paused":"",d=$+"PlayState";la(a,[d,c]);return[d,c]}function la(a,b){a.style[b[0]]=
|
||||
b[1]}function Z(a,b){return a?b?a+" "+b:a:b}function Fa(a,b,c){var d=Object.create(null),e=a.getComputedStyle(b)||{};s(c,function(a,b){var c=e[a];if(c){var k=c.charAt(0);if("-"===k||"+"===k||0<=k)c=Ta(c);0===c&&(c=null);d[b]=c}});return d}function Ta(a){var b=0;a=a.split(/\s*,\s*/);s(a,function(a){"s"==a.charAt(a.length-1)&&(a=a.substring(0,a.length-1));a=parseFloat(a)||0;b=b?Math.max(a,b):a});return b}function ua(a){return 0===a||null!=a}function Ga(a,b){var c=T,d=a+"s";b?c+="Duration":d+=" linear all";
|
||||
return[c,d]}function Ha(){var a=Object.create(null);return{flush:function(){a=Object.create(null)},count:function(b){return(b=a[b])?b.total:0},get:function(b){return(b=a[b])&&b.value},put:function(b,c){a[b]?a[b].total++:a[b]={total:1,value:c}}}}function Ia(a,b,c){s(c,function(c){a[c]=ea(a[c])?a[c]:b.style.getPropertyValue(c)})}var R=B.noop,Ja=B.copy,Ea=B.extend,E=B.element,s=B.forEach,ca=B.isArray,P=B.isString,va=B.isObject,O=B.isUndefined,ea=B.isDefined,Ka=B.isFunction,wa=B.isElement,T,xa,$,ya;O(S.ontransitionend)&&
|
||||
ea(S.onwebkittransitionend)?(T="WebkitTransition",xa="webkitTransitionEnd transitionend"):(T="transition",xa="transitionend");O(S.onanimationend)&&ea(S.onwebkitanimationend)?($="WebkitAnimation",ya="webkitAnimationEnd animationend"):($="animation",ya="animationend");var ra=$+"Delay",za=$+"Duration",ma=T+"Delay",La=T+"Duration",Ma=B.$$minErr("ng"),Ua={transitionDuration:La,transitionDelay:ma,transitionProperty:T+"Property",animationDuration:za,animationDelay:ra,animationIterationCount:$+"IterationCount"},
|
||||
Va={transitionDuration:La,transitionDelay:ma,animationDuration:za,animationDelay:ra};B.module("ngAnimate",[]).directive("ngAnimateSwap",["$animate","$rootScope",function(a,b){return{restrict:"A",transclude:"element",terminal:!0,priority:600,link:function(b,d,e,l,m){var I,k;b.$watchCollection(e.ngAnimateSwap||e["for"],function(e){I&&a.leave(I);k&&(k.$destroy(),k=null);if(e||0===e)k=b.$new(),m(k,function(b){I=b;a.enter(b,null,d)})})}}}]).directive("ngAnimateChildren",["$interpolate",function(a){return{link:function(b,
|
||||
c,d){function e(a){c.data("$$ngAnimateChildren","on"===a||"true"===a)}var l=d.ngAnimateChildren;B.isString(l)&&0===l.length?c.data("$$ngAnimateChildren",!0):(e(a(l)(b)),d.$observe("ngAnimateChildren",e))}}}]).factory("$$rAFScheduler",["$$rAF",function(a){function b(a){d=d.concat(a);c()}function c(){if(d.length){for(var b=d.shift(),m=0;m<b.length;m++)b[m]();e||a(function(){e||c()})}}var d,e;d=b.queue=[];b.waitUntilQuiet=function(b){e&&e();e=a(function(){e=null;b();c()})};return b}]).provider("$$animateQueue",
|
||||
["$animateProvider",function(a){function b(a){if(!a)return null;a=a.split(" ");var b=Object.create(null);s(a,function(a){b[a]=!0});return b}function c(a,c){if(a&&c){var d=b(c);return a.split(" ").some(function(a){return d[a]})}}function d(a,b,c,d){return l[a].some(function(a){return a(b,c,d)})}function e(a,b){var c=0<(a.addClass||"").length,d=0<(a.removeClass||"").length;return b?c&&d:c||d}var l=this.rules={skip:[],cancel:[],join:[]};l.join.push(function(a,b,c){return!b.structural&&e(b)});l.skip.push(function(a,
|
||||
b,c){return!b.structural&&!e(b)});l.skip.push(function(a,b,c){return"leave"==c.event&&b.structural});l.skip.push(function(a,b,c){return c.structural&&2===c.state&&!b.structural});l.cancel.push(function(a,b,c){return c.structural&&b.structural});l.cancel.push(function(a,b,c){return 2===c.state&&b.structural});l.cancel.push(function(a,b,d){if(d.structural)return!1;a=b.addClass;b=b.removeClass;var e=d.addClass;d=d.removeClass;return O(a)&&O(b)||O(e)&&O(d)?!1:c(a,d)||c(b,e)});this.$get=["$$rAF","$rootScope",
|
||||
"$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(b,c,k,l,w,Wa,Q,u,F,n){function K(){var a=!1;return function(b){a?b():c.$$postDigest(function(){a=!0;b()})}}function J(a,b,c){var f=z(b),d=z(a),L=[];(a=h[c])&&s(a,function(a){y.call(a.node,f)?L.push(a.callback):"leave"===c&&y.call(a.node,d)&&L.push(a.callback)});return L}function r(a,b,c){var f=da(b);return a.filter(function(a){return!(a.node===f&&(!c||a.callback===c))})}function t(a,
|
||||
h,x){function r(c,f,d,h){H(function(){var c=J(oa,a,f);c.length?b(function(){s(c,function(b){b(a,d,h)});"close"!==d||a[0].parentNode||sa.off(a)}):"close"!==d||a[0].parentNode||sa.off(a)});c.progress(f,d,h)}function g(b){var c=a,f=p;f.preparationClasses&&(c.removeClass(f.preparationClasses),f.preparationClasses=null);f.activeClasses&&(c.removeClass(f.activeClasses),f.activeClasses=null);D(a,p);ha(a,p);p.domOperation();n.complete(!b)}var p=Ja(x),t,oa;if(a=Oa(a))t=z(a),oa=a.parent();var p=pa(p),n=new Q,
|
||||
H=K();ca(p.addClass)&&(p.addClass=p.addClass.join(" "));p.addClass&&!P(p.addClass)&&(p.addClass=null);ca(p.removeClass)&&(p.removeClass=p.removeClass.join(" "));p.removeClass&&!P(p.removeClass)&&(p.removeClass=null);p.from&&!va(p.from)&&(p.from=null);p.to&&!va(p.to)&&(p.to=null);if(!t)return g(),n;x=[t.className,p.addClass,p.removeClass].join(" ");if(!Xa(x))return g(),n;var k=0<=["enter","move","leave"].indexOf(h),y=l[0].hidden,u=!f||y||L.get(t);x=!u&&A.get(t)||{};var F=!!x.state;u||F&&1==x.state||
|
||||
(u=!N(a,oa,h));if(u)return y&&r(n,h,"start"),g(),y&&r(n,h,"close"),n;k&&M(a);y={structural:k,element:a,event:h,addClass:p.addClass,removeClass:p.removeClass,close:g,options:p,runner:n};if(F){if(d("skip",a,y,x)){if(2===x.state)return g(),n;W(a,x,y);return x.runner}if(d("cancel",a,y,x))if(2===x.state)x.runner.end();else if(x.structural)x.close();else return W(a,x,y),x.runner;else if(d("join",a,y,x))if(2===x.state)W(a,y,{});else return Sa(a,k?h:null,p),h=y.event=x.event,p=W(a,x,y),x.runner}else W(a,
|
||||
y,{});(F=y.structural)||(F="animate"===y.event&&0<Object.keys(y.options.to||{}).length||e(y));if(!F)return g(),ka(a),n;var w=(x.counter||0)+1;y.counter=w;G(a,1,y);c.$$postDigest(function(){var b=A.get(t),c=!b,b=b||{},f=0<(a.parent()||[]).length&&("animate"===b.event||b.structural||e(b));if(c||b.counter!==w||!f){c&&(D(a,p),ha(a,p));if(c||k&&b.event!==h)p.domOperation(),n.end();f||ka(a)}else h=!b.structural&&e(b,!0)?"setClass":b.event,G(a,2),b=Wa(a,h,b.options),n.setHost(b),r(n,h,"start",{}),b.done(function(b){g(!b);
|
||||
(b=A.get(t))&&b.counter===w&&ka(z(a));r(n,h,"close",{})})});return n}function M(a){a=z(a).querySelectorAll("[data-ng-animate]");s(a,function(a){var b=parseInt(a.getAttribute("data-ng-animate")),c=A.get(a);if(c)switch(b){case 2:c.runner.end();case 1:A.remove(a)}})}function ka(a){a=z(a);a.removeAttribute("data-ng-animate");A.remove(a)}function g(a,b){return z(a)===z(b)}function N(a,b,c){c=E(l[0].body);var f=g(a,c)||"HTML"===a[0].nodeName,d=g(a,k),h=!1,r,e=L.get(z(a));(a=E.data(a[0],"$ngAnimatePin"))&&
|
||||
(b=a);for(b=z(b);b;){d||(d=g(b,k));if(1!==b.nodeType)break;a=A.get(b)||{};if(!h){var n=L.get(b);if(!0===n&&!1!==e){e=!0;break}else!1===n&&(e=!1);h=a.structural}if(O(r)||!0===r)a=E.data(b,"$$ngAnimateChildren"),ea(a)&&(r=a);if(h&&!1===r)break;f||(f=g(b,c));if(f&&d)break;if(!d&&(a=E.data(b,"$ngAnimatePin"))){b=z(a);continue}b=b.parentNode}return(!h||r)&&!0!==e&&d&&f}function G(a,b,c){c=c||{};c.state=b;a=z(a);a.setAttribute("data-ng-animate",b);c=(b=A.get(a))?Ea(b,c):c;A.put(a,c)}var A=new w,L=new w,
|
||||
f=null,oa=c.$watch(function(){return 0===u.totalPendingRequests},function(a){a&&(oa(),c.$$postDigest(function(){c.$$postDigest(function(){null===f&&(f=!0)})}))}),h={},H=a.classNameFilter(),Xa=H?function(a){return H.test(a)}:function(){return!0},D=V(F),y=S.Node.prototype.contains||function(a){return this===a||!!(this.compareDocumentPosition(a)&16)},sa={on:function(a,b,c){var f=da(b);h[a]=h[a]||[];h[a].push({node:f,callback:c});E(b).on("$destroy",function(){A.get(f)||sa.off(a,b,c)})},off:function(a,
|
||||
b,c){if(1!==arguments.length||B.isString(arguments[0])){var f=h[a];f&&(h[a]=1===arguments.length?null:r(f,b,c))}else for(f in b=arguments[0],h)h[f]=r(h[f],b)},pin:function(a,b){Aa(wa(a),"element","not an element");Aa(wa(b),"parentElement","not an element");a.data("$ngAnimatePin",b)},push:function(a,b,c,f){c=c||{};c.domOperation=f;return t(a,b,c)},enabled:function(a,b){var c=arguments.length;if(0===c)b=!!f;else if(wa(a)){var d=z(a),h=L.get(d);1===c?b=!h:L.put(d,!b)}else b=f=!!a;return b}};return sa}]}]).provider("$$animation",
|
||||
["$animateProvider",function(a){var b=this.drivers=[];this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(a,d,e,l,m,I){function k(a){function b(a){if(a.processed)return a;a.processed=!0;var d=a.domNode,t=d.parentNode;e.put(d,a);for(var M;t;){if(M=e.get(t)){M.processed||(M=b(M));break}t=t.parentNode}(M||c).children.push(a);return a}var c={children:[]},d,e=new m;for(d=0;d<a.length;d++){var k=a[d];e.put(k.domNode,a[d]={domNode:k.domNode,fn:k.fn,children:[]})}for(d=
|
||||
0;d<a.length;d++)b(a[d]);return function(a){var b=[],c=[],d;for(d=0;d<a.children.length;d++)c.push(a.children[d]);a=c.length;var e=0,g=[];for(d=0;d<c.length;d++){var n=c[d];0>=a&&(a=e,e=0,b.push(g),g=[]);g.push(n.fn);n.children.forEach(function(a){e++;c.push(a)});a--}g.length&&b.push(g);return b}(c)}var v=[],w=V(a);return function(m,Q,u){function F(a){a=a.hasAttribute("ng-animate-ref")?[a]:a.querySelectorAll("[ng-animate-ref]");var b=[];s(a,function(a){var c=a.getAttribute("ng-animate-ref");c&&c.length&&
|
||||
b.push(a)});return b}function n(a){var b=[],c={};s(a,function(a,d){var h=z(a.element),e=0<=["enter","move"].indexOf(a.event),h=a.structural?F(h):[];if(h.length){var g=e?"to":"from";s(h,function(a){var b=a.getAttribute("ng-animate-ref");c[b]=c[b]||{};c[b][g]={animationID:d,element:E(a)}})}else b.push(a)});var d={},e={};s(c,function(c,g){var r=c.from,n=c.to;if(r&&n){var A=a[r.animationID],t=a[n.animationID],m=r.animationID.toString();if(!e[m]){var k=e[m]={structural:!0,beforeStart:function(){A.beforeStart();
|
||||
t.beforeStart()},close:function(){A.close();t.close()},classes:K(A.classes,t.classes),from:A,to:t,anchors:[]};k.classes.length?b.push(k):(b.push(A),b.push(t))}e[m].anchors.push({out:r.element,"in":n.element})}else r=r?r.animationID:n.animationID,n=r.toString(),d[n]||(d[n]=!0,b.push(a[r]))});return b}function K(a,b){a=a.split(" ");b=b.split(" ");for(var c=[],d=0;d<a.length;d++){var e=a[d];if("ng-"!==e.substring(0,3))for(var r=0;r<b.length;r++)if(e===b[r]){c.push(e);break}}return c.join(" ")}function J(a){for(var c=
|
||||
b.length-1;0<=c;c--){var d=b[c];if(e.has(d)&&(d=e.get(d)(a)))return d}}function r(a,b){function c(a){(a=a.data("$$animationRunner"))&&a.setHost(b)}a.from&&a.to?(c(a.from.element),c(a.to.element)):c(a.element)}function t(){var a=m.data("$$animationRunner");!a||"leave"===Q&&u.$$domOperationFired||a.end()}function M(b){m.off("$destroy",t);m.removeData("$$animationRunner");w(m,u);ha(m,u);u.domOperation();G&&a.removeClass(m,G);m.removeClass("ng-animate");g.complete(!b)}u=pa(u);var ka=0<=["enter","move",
|
||||
"leave"].indexOf(Q),g=new l({end:function(){M()},cancel:function(){M(!0)}});if(!b.length)return M(),g;m.data("$$animationRunner",g);var N=Ba(m.attr("class"),Ba(u.addClass,u.removeClass)),G=u.tempClasses;G&&(N+=" "+G,u.tempClasses=null);var A;ka&&(A="ng-"+Q+"-prepare",a.addClass(m,A));v.push({element:m,classes:N,event:Q,structural:ka,options:u,beforeStart:function(){m.addClass("ng-animate");G&&a.addClass(m,G);A&&(a.removeClass(m,A),A=null)},close:M});m.on("$destroy",t);if(1<v.length)return g;d.$$postDigest(function(){var a=
|
||||
[];s(v,function(b){b.element.data("$$animationRunner")?a.push(b):b.close()});v.length=0;var b=n(a),c=[];s(b,function(a){c.push({domNode:z(a.from?a.from.element:a.element),fn:function(){a.beforeStart();var b,c=a.close;if((a.anchors?a.from.element||a.to.element:a.element).data("$$animationRunner")){var d=J(a);d&&(b=d.start)}b?(b=b(),b.done(function(a){c(!a)}),r(a,b)):c()}})});I(k(c))});return g}}]}]).provider("$animateCss",["$animateProvider",function(a){var b=Ha(),c=Ha();this.$get=["$window","$$jqLite",
|
||||
"$$AnimateRunner","$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(a,e,l,m,I,k,v,w){function B(a,b){var c=a.parentNode;return(c.$$ngAnimateParentKey||(c.$$ngAnimateParentKey=++K))+"-"+a.getAttribute("class")+"-"+b}function Q(r,n,m,k){var g;0<b.count(m)&&(g=c.get(m),g||(n=Y(n,"-stagger"),e.addClass(r,n),g=Fa(a,r,k),g.animationDuration=Math.max(g.animationDuration,0),g.transitionDuration=Math.max(g.transitionDuration,0),e.removeClass(r,n),c.put(m,g)));return g||{}}function u(a){J.push(a);
|
||||
v.waitUntilQuiet(function(){b.flush();c.flush();for(var a=I(),d=0;d<J.length;d++)J[d](a);J.length=0})}function F(c,e,n){e=b.get(n);e||(e=Fa(a,c,Ua),"infinite"===e.animationIterationCount&&(e.animationIterationCount=1));b.put(n,e);c=e;n=c.animationDelay;e=c.transitionDelay;c.maxDelay=n&&e?Math.max(n,e):n||e;c.maxDuration=Math.max(c.animationDuration*c.animationIterationCount,c.transitionDuration);return c}var n=V(e),K=0,J=[];return function(a,c){function d(){g()}function v(){g(!0)}function g(b){if(!(y||
|
||||
E&&K)){y=!0;K=!1;f.$$skipPreparationClasses||e.removeClass(a,ga);e.removeClass(a,ea);ta(h,!1);qa(h,!1);s(H,function(a){h.style[a[0]]=""});n(a,f);ha(a,f);Object.keys(J).length&&s(J,function(a,b){a?h.style.setProperty(b,a):h.style.removeProperty(b)});if(f.onDone)f.onDone();fa&&fa.length&&a.off(fa.join(" "),A);var c=a.data("$$animateCss");c&&(m.cancel(c[0].timer),a.removeData("$$animateCss"));O&&O.complete(!b)}}function N(a){q.blockTransition&&qa(h,a);q.blockKeyframeAnimation&&ta(h,!!a)}function G(){O=
|
||||
new l({end:d,cancel:v});u(R);g();return{$$willAnimate:!1,start:function(){return O},end:d}}function A(a){a.stopPropagation();var b=a.originalEvent||a;a=b.$manualTimeStamp||Date.now();b=parseFloat(b.elapsedTime.toFixed(3));Math.max(a-W,0)>=S&&b>=p&&(E=!0,g())}function L(){function b(){if(!y){N(!1);s(H,function(a){h.style[a[0]]=a[1]});n(a,f);e.addClass(a,ea);if(q.recalculateTimingStyles){na=h.className+" "+ga;ia=B(h,na);C=F(h,na,ia);aa=C.maxDelay;P=Math.max(aa,0);p=C.maxDuration;if(0===p){g();return}q.hasTransitions=
|
||||
0<C.transitionDuration;q.hasAnimations=0<C.animationDuration}q.applyAnimationDelay&&(aa="boolean"!==typeof f.delay&&ua(f.delay)?parseFloat(f.delay):aa,P=Math.max(aa,0),C.animationDelay=aa,ba=[ra,aa+"s"],H.push(ba),h.style[ba[0]]=ba[1]);S=1E3*P;V=1E3*p;if(f.easing){var d,k=f.easing;q.hasTransitions&&(d=T+"TimingFunction",H.push([d,k]),h.style[d]=k);q.hasAnimations&&(d=$+"TimingFunction",H.push([d,k]),h.style[d]=k)}C.transitionDuration&&fa.push(xa);C.animationDuration&&fa.push(ya);W=Date.now();var L=
|
||||
S+1.5*V;d=W+L;var k=a.data("$$animateCss")||[],l=!0;if(k.length){var G=k[0];(l=d>G.expectedEndTime)?m.cancel(G.timer):k.push(g)}l&&(L=m(c,L,!1),k[0]={timer:L,expectedEndTime:d},k.push(g),a.data("$$animateCss",k));if(fa.length)a.on(fa.join(" "),A);f.to&&(f.cleanupStyles&&Ia(J,h,Object.keys(f.to)),Da(a,f))}}function c(){var b=a.data("$$animateCss");if(b){for(var d=1;d<b.length;d++)b[d]();a.removeData("$$animateCss")}}if(!y)if(h.parentNode){var d=function(a){if(E)K&&a&&(K=!1,g());else if(K=!a,C.animationDuration)if(a=
|
||||
ta(h,K),K)H.push(a);else{var b=H,c=b.indexOf(a);0<=a&&b.splice(c,1)}},k=0<da&&(C.transitionDuration&&0===X.transitionDuration||C.animationDuration&&0===X.animationDuration)&&Math.max(X.animationDelay,X.transitionDelay);k?m(b,Math.floor(k*da*1E3),!1):b();x.resume=function(){d(!0)};x.pause=function(){d(!1)}}else g()}var f=c||{};f.$$prepared||(f=pa(Ja(f)));var J={},h=z(a);if(!h||!h.parentNode||!w.enabled())return G();var H=[],I=a.attr("class"),D=Na(f),y,K,E,O,x,P,S,p,V,W,fa=[];if(0===f.duration||!k.animations&&
|
||||
!k.transitions)return G();var ja=f.event&&ca(f.event)?f.event.join(" "):f.event,Z="",U="";ja&&f.structural?Z=Y(ja,"ng-",!0):ja&&(Z=ja);f.addClass&&(U+=Y(f.addClass,"-add"));f.removeClass&&(U.length&&(U+=" "),U+=Y(f.removeClass,"-remove"));f.applyClassesEarly&&U.length&&n(a,f);var ga=[Z,U].join(" ").trim(),na=I+" "+ga,ea=Y(ga,"-active"),I=D.to&&0<Object.keys(D.to).length;if(!(0<(f.keyframeStyle||"").length||I||ga))return G();var ia,X;0<f.stagger?(D=parseFloat(f.stagger),X={transitionDelay:D,animationDelay:D,
|
||||
transitionDuration:0,animationDuration:0}):(ia=B(h,na),X=Q(h,ga,ia,Va));f.$$skipPreparationClasses||e.addClass(a,ga);f.transitionStyle&&(D=[T,f.transitionStyle],la(h,D),H.push(D));0<=f.duration&&(D=0<h.style[T].length,D=Ga(f.duration,D),la(h,D),H.push(D));f.keyframeStyle&&(D=[$,f.keyframeStyle],la(h,D),H.push(D));var da=X?0<=f.staggerIndex?f.staggerIndex:b.count(ia):0;(ja=0===da)&&!f.skipBlocking&&qa(h,9999);var C=F(h,na,ia),aa=C.maxDelay;P=Math.max(aa,0);p=C.maxDuration;var q={};q.hasTransitions=
|
||||
0<C.transitionDuration;q.hasAnimations=0<C.animationDuration;q.hasTransitionAll=q.hasTransitions&&"all"==C.transitionProperty;q.applyTransitionDuration=I&&(q.hasTransitions&&!q.hasTransitionAll||q.hasAnimations&&!q.hasTransitions);q.applyAnimationDuration=f.duration&&q.hasAnimations;q.applyTransitionDelay=ua(f.delay)&&(q.applyTransitionDuration||q.hasTransitions);q.applyAnimationDelay=ua(f.delay)&&q.hasAnimations;q.recalculateTimingStyles=0<U.length;if(q.applyTransitionDuration||q.applyAnimationDuration)p=
|
||||
f.duration?parseFloat(f.duration):p,q.applyTransitionDuration&&(q.hasTransitions=!0,C.transitionDuration=p,D=0<h.style[T+"Property"].length,H.push(Ga(p,D))),q.applyAnimationDuration&&(q.hasAnimations=!0,C.animationDuration=p,H.push([za,p+"s"]));if(0===p&&!q.recalculateTimingStyles)return G();if(null!=f.delay){var ba;"boolean"!==typeof f.delay&&(ba=parseFloat(f.delay),P=Math.max(ba,0));q.applyTransitionDelay&&H.push([ma,ba+"s"]);q.applyAnimationDelay&&H.push([ra,ba+"s"])}null==f.duration&&0<C.transitionDuration&&
|
||||
(q.recalculateTimingStyles=q.recalculateTimingStyles||ja);S=1E3*P;V=1E3*p;f.skipBlocking||(q.blockTransition=0<C.transitionDuration,q.blockKeyframeAnimation=0<C.animationDuration&&0<X.animationDelay&&0===X.animationDuration);f.from&&(f.cleanupStyles&&Ia(J,h,Object.keys(f.from)),Ca(a,f));q.blockTransition||q.blockKeyframeAnimation?N(p):f.skipBlocking||qa(h,!1);return{$$willAnimate:!0,end:d,start:function(){if(!y)return x={end:d,cancel:v,resume:null,pause:null},O=new l(x),u(L),O}}}}]}]).provider("$$animateCssDriver",
|
||||
["$$animationProvider",function(a){a.drivers.push("$$animateCssDriver");this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(a,c,d,e,l,m,I){function k(a){return a.replace(/\bng-\S+\b/g,"")}function v(a,b){P(a)&&(a=a.split(" "));P(b)&&(b=b.split(" "));return a.filter(function(a){return-1===b.indexOf(a)}).join(" ")}function w(c,e,m){function l(a){var b={},c=z(a).getBoundingClientRect();s(["width","height","top","left"],function(a){var d=c[a];
|
||||
switch(a){case "top":d+=u.scrollTop;break;case "left":d+=u.scrollLeft}b[a]=Math.floor(d)+"px"});return b}function t(){var c=k(m.attr("class")||""),d=v(c,g),c=v(g,c),d=a(w,{to:l(m),addClass:"ng-anchor-in "+d,removeClass:"ng-anchor-out "+c,delay:!0});return d.$$willAnimate?d:null}function I(){w.remove();e.removeClass("ng-animate-shim");m.removeClass("ng-animate-shim")}var w=E(z(e).cloneNode(!0)),g=k(w.attr("class")||"");e.addClass("ng-animate-shim");m.addClass("ng-animate-shim");w.addClass("ng-anchor");
|
||||
F.append(w);var N;c=function(){var c=a(w,{addClass:"ng-anchor-out",delay:!0,from:l(e)});return c.$$willAnimate?c:null}();if(!c&&(N=t(),!N))return I();var G=c||N;return{start:function(){function a(){c&&c.end()}var b,c=G.start();c.done(function(){c=null;if(!N&&(N=t()))return c=N.start(),c.done(function(){c=null;I();b.complete()}),c;I();b.complete()});return b=new d({end:a,cancel:a})}}}function B(a,b,c,e){var k=Q(a,R),m=Q(b,R),l=[];s(e,function(a){(a=w(c,a.out,a["in"]))&&l.push(a)});if(k||m||0!==l.length)return{start:function(){function a(){s(b,
|
||||
function(a){a.end()})}var b=[];k&&b.push(k.start());m&&b.push(m.start());s(l,function(a){b.push(a.start())});var c=new d({end:a,cancel:a});d.all(b,function(a){c.complete(a)});return c}}}function Q(c){var d=c.element,e=c.options||{};c.structural&&(e.event=c.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===c.event&&(e.onDone=e.domOperation));e.preparationClasses&&(e.event=Z(e.event,e.preparationClasses));c=a(d,e);return c.$$willAnimate?c:null}if(!l.animations&&!l.transitions)return R;var u=I[0].body;
|
||||
c=z(e);var F=E(c.parentNode&&11===c.parentNode.nodeType||u.contains(c)?c:u);V(m);return function(a){return a.from&&a.to?B(a.from,a.to,a.classes,a.anchors):Q(a)}}]}]).provider("$$animateJs",["$animateProvider",function(a){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(b,c,d){function e(c){c=ca(c)?c:c.split(" ");for(var d=[],e={},l=0;l<c.length;l++){var w=c[l],s=a.$$registeredAnimations[w];s&&!e[w]&&(d.push(b.get(s)),e[w]=!0)}return d}var l=V(d);return function(a,b,d,v){function w(){v.domOperation();
|
||||
l(a,v)}function B(a,b,d,e,f){switch(d){case "animate":b=[b,e.from,e.to,f];break;case "setClass":b=[b,n,K,f];break;case "addClass":b=[b,n,f];break;case "removeClass":b=[b,K,f];break;default:b=[b,f]}b.push(e);if(a=a.apply(a,b))if(Ka(a.start)&&(a=a.start()),a instanceof c)a.done(f);else if(Ka(a))return a;return R}function z(a,b,d,e,f){var g=[];s(e,function(e){var k=e[f];k&&g.push(function(){var e,f,h=!1,g=function(a){h||(h=!0,(f||R)(a),e.complete(!a))};e=new c({end:function(){g()},cancel:function(){g(!0)}});
|
||||
f=B(k,a,b,d,function(a){g(!1===a)});return e})});return g}function u(a,b,d,e,f){var g=z(a,b,d,e,f);if(0===g.length){var h,k;"beforeSetClass"===f?(h=z(a,"removeClass",d,e,"beforeRemoveClass"),k=z(a,"addClass",d,e,"beforeAddClass")):"setClass"===f&&(h=z(a,"removeClass",d,e,"removeClass"),k=z(a,"addClass",d,e,"addClass"));h&&(g=g.concat(h));k&&(g=g.concat(k))}if(0!==g.length)return function(a){var b=[];g.length&&s(g,function(a){b.push(a())});b.length?c.all(b,a):a();return function(a){s(b,function(b){a?
|
||||
b.cancel():b.end()})}}}var F=!1;3===arguments.length&&va(d)&&(v=d,d=null);v=pa(v);d||(d=a.attr("class")||"",v.addClass&&(d+=" "+v.addClass),v.removeClass&&(d+=" "+v.removeClass));var n=v.addClass,K=v.removeClass,J=e(d),r,t;if(J.length){var M,E;"leave"==b?(E="leave",M="afterLeave"):(E="before"+b.charAt(0).toUpperCase()+b.substr(1),M=b);"enter"!==b&&"move"!==b&&(r=u(a,b,v,J,E));t=u(a,b,v,J,M)}if(r||t){var g;return{$$willAnimate:!0,end:function(){g?g.end():(F=!0,w(),ha(a,v),g=new c,g.complete(!0));return g},
|
||||
start:function(){function b(c){F=!0;w();ha(a,v);g.complete(c)}if(g)return g;g=new c;var d,e=[];r&&e.push(function(a){d=r(a)});e.length?e.push(function(a){w();a(!0)}):w();t&&e.push(function(a){d=t(a)});g.setHost({end:function(){F||((d||R)(void 0),b(void 0))},cancel:function(){F||((d||R)(!0),b(!0))}});c.chain(e,b);return g}}}}}]}]).provider("$$animateJsDriver",["$$animationProvider",function(a){a.drivers.push("$$animateJsDriver");this.$get=["$$animateJs","$$AnimateRunner",function(a,c){function d(c){return a(c.element,
|
||||
c.event,c.classes,c.options)}return function(a){if(a.from&&a.to){var b=d(a.from),m=d(a.to);if(b||m)return{start:function(){function a(){return function(){s(d,function(a){a.end()})}}var d=[];b&&d.push(b.start());m&&d.push(m.start());c.all(d,function(a){e.complete(a)});var e=new c({end:a(),cancel:a()});return e}}}else return d(a)}}]}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-animate.min.js.map
|
||||
8
src/main/resources/static/vendor/angular/angular-animate.min.js.map
vendored
Normal file
8
src/main/resources/static/vendor/angular/angular-animate.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
405
src/main/resources/static/vendor/angular/angular-aria.js
vendored
Normal file
405
src/main/resources/static/vendor/angular/angular-aria.js
vendored
Normal file
@@ -0,0 +1,405 @@
|
||||
/**
|
||||
* @license AngularJS v1.5.6
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngAria
|
||||
* @description
|
||||
*
|
||||
* The `ngAria` module provides support for common
|
||||
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
|
||||
* attributes that convey state or semantic information about the application for users
|
||||
* of assistive technologies, such as screen readers.
|
||||
*
|
||||
* <div doc-module-components="ngAria"></div>
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
|
||||
* directives are supported:
|
||||
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
|
||||
* `ngDblClick`, and `ngMessages`.
|
||||
*
|
||||
* Below is a more detailed breakdown of the attributes handled by ngAria:
|
||||
*
|
||||
* | Directive | Supported Attributes |
|
||||
* |---------------------------------------------|----------------------------------------------------------------------------------------|
|
||||
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
|
||||
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
|
||||
* | {@link ng.directive:ngRequired ngRequired} | aria-required
|
||||
* | {@link ng.directive:ngChecked ngChecked} | aria-checked
|
||||
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly |
|
||||
* | {@link ng.directive:ngValue ngValue} | aria-checked |
|
||||
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
|
||||
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
|
||||
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
|
||||
* | {@link module:ngMessages ngMessages} | aria-live |
|
||||
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role |
|
||||
*
|
||||
* Find out more information about each directive by reading the
|
||||
* {@link guide/accessibility ngAria Developer Guide}.
|
||||
*
|
||||
* ## Example
|
||||
* Using ngDisabled with ngAria:
|
||||
* ```html
|
||||
* <md-checkbox ng-disabled="disabled">
|
||||
* ```
|
||||
* Becomes:
|
||||
* ```html
|
||||
* <md-checkbox ng-disabled="disabled" aria-disabled="true">
|
||||
* ```
|
||||
*
|
||||
* ## Disabling Attributes
|
||||
* It's possible to disable individual attributes added by ngAria with the
|
||||
* {@link ngAria.$ariaProvider#config config} method. For more details, see the
|
||||
* {@link guide/accessibility Developer Guide}.
|
||||
*/
|
||||
/* global -ngAriaModule */
|
||||
var ngAriaModule = angular.module('ngAria', ['ng']).
|
||||
provider('$aria', $AriaProvider);
|
||||
|
||||
/**
|
||||
* Internal Utilities
|
||||
*/
|
||||
var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
|
||||
|
||||
var isNodeOneOf = function(elem, nodeTypeArray) {
|
||||
if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $ariaProvider
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Used for configuring the ARIA attributes injected and managed by ngAria.
|
||||
*
|
||||
* ```js
|
||||
* angular.module('myApp', ['ngAria'], function config($ariaProvider) {
|
||||
* $ariaProvider.config({
|
||||
* ariaValue: true,
|
||||
* tabindex: false
|
||||
* });
|
||||
* });
|
||||
*```
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngAria} module to be installed.
|
||||
*
|
||||
*/
|
||||
function $AriaProvider() {
|
||||
var config = {
|
||||
ariaHidden: true,
|
||||
ariaChecked: true,
|
||||
ariaReadonly: true,
|
||||
ariaDisabled: true,
|
||||
ariaRequired: true,
|
||||
ariaInvalid: true,
|
||||
ariaValue: true,
|
||||
tabindex: true,
|
||||
bindKeypress: true,
|
||||
bindRoleForClick: true
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $ariaProvider#config
|
||||
*
|
||||
* @param {object} config object to enable/disable specific ARIA attributes
|
||||
*
|
||||
* - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
|
||||
* - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
|
||||
* - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags
|
||||
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
|
||||
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
|
||||
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
|
||||
* - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
|
||||
* - **tabindex** – `{boolean}` – Enables/disables tabindex tags
|
||||
* - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
|
||||
* `li` elements with ng-click
|
||||
* - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
|
||||
* using ng-click, making them more accessible to users of assistive technologies
|
||||
*
|
||||
* @description
|
||||
* Enables/disables various ARIA attributes
|
||||
*/
|
||||
this.config = function(newConfig) {
|
||||
config = angular.extend(config, newConfig);
|
||||
};
|
||||
|
||||
function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
|
||||
return function(scope, elem, attr) {
|
||||
var ariaCamelName = attr.$normalize(ariaAttr);
|
||||
if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
|
||||
scope.$watch(attr[attrName], function(boolVal) {
|
||||
// ensure boolean value
|
||||
boolVal = negate ? !boolVal : !!boolVal;
|
||||
elem.attr(ariaAttr, boolVal);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $aria
|
||||
*
|
||||
* @description
|
||||
* @priority 200
|
||||
*
|
||||
* The $aria service contains helper methods for applying common
|
||||
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
|
||||
*
|
||||
* ngAria injects common accessibility attributes that tell assistive technologies when HTML
|
||||
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
|
||||
* let's review a code snippet from ngAria itself:
|
||||
*
|
||||
*```js
|
||||
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
|
||||
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
|
||||
* }])
|
||||
*```
|
||||
* Shown above, the ngAria module creates a directive with the same signature as the
|
||||
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
|
||||
* solely managing accessibility attributes on custom elements. The internal `$aria` service is
|
||||
* used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
|
||||
* developer, `aria-disabled` is injected as an attribute with its value synchronized to the
|
||||
* value in `ngDisabled`.
|
||||
*
|
||||
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
|
||||
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
|
||||
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
|
||||
*
|
||||
* The full list of directives that interface with ngAria:
|
||||
* * **ngModel**
|
||||
* * **ngChecked**
|
||||
* * **ngReadonly**
|
||||
* * **ngRequired**
|
||||
* * **ngDisabled**
|
||||
* * **ngValue**
|
||||
* * **ngShow**
|
||||
* * **ngHide**
|
||||
* * **ngClick**
|
||||
* * **ngDblclick**
|
||||
* * **ngMessages**
|
||||
*
|
||||
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
|
||||
* directive.
|
||||
*
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngAria} module to be installed.
|
||||
*/
|
||||
this.$get = function() {
|
||||
return {
|
||||
config: function(key) {
|
||||
return config[key];
|
||||
},
|
||||
$$watchExpr: watchExpr
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
|
||||
}])
|
||||
.directive('ngHide', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
|
||||
}])
|
||||
.directive('ngValue', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngChecked', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngReadonly', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngRequired', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngModel', ['$aria', function($aria) {
|
||||
|
||||
function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
|
||||
return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
|
||||
}
|
||||
|
||||
function shouldAttachRole(role, elem) {
|
||||
// if element does not have role attribute
|
||||
// AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
|
||||
// AND element is not INPUT
|
||||
return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
|
||||
}
|
||||
|
||||
function getShape(attr, elem) {
|
||||
var type = attr.type,
|
||||
role = attr.role;
|
||||
|
||||
return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
|
||||
((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' :
|
||||
(type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : '';
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
|
||||
compile: function(elem, attr) {
|
||||
var shape = getShape(attr, elem);
|
||||
|
||||
return {
|
||||
pre: function(scope, elem, attr, ngModel) {
|
||||
if (shape === 'checkbox') {
|
||||
//Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
|
||||
ngModel.$isEmpty = function(value) {
|
||||
return value === false;
|
||||
};
|
||||
}
|
||||
},
|
||||
post: function(scope, elem, attr, ngModel) {
|
||||
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
|
||||
|
||||
function ngAriaWatchModelValue() {
|
||||
return ngModel.$modelValue;
|
||||
}
|
||||
|
||||
function getRadioReaction(newVal) {
|
||||
var boolVal = (attr.value == ngModel.$viewValue);
|
||||
elem.attr('aria-checked', boolVal);
|
||||
}
|
||||
|
||||
function getCheckboxReaction() {
|
||||
elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
|
||||
}
|
||||
|
||||
switch (shape) {
|
||||
case 'radio':
|
||||
case 'checkbox':
|
||||
if (shouldAttachRole(shape, elem)) {
|
||||
elem.attr('role', shape);
|
||||
}
|
||||
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
|
||||
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
|
||||
getRadioReaction : getCheckboxReaction);
|
||||
}
|
||||
if (needsTabIndex) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
break;
|
||||
case 'range':
|
||||
if (shouldAttachRole(shape, elem)) {
|
||||
elem.attr('role', 'slider');
|
||||
}
|
||||
if ($aria.config('ariaValue')) {
|
||||
var needsAriaValuemin = !elem.attr('aria-valuemin') &&
|
||||
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
|
||||
var needsAriaValuemax = !elem.attr('aria-valuemax') &&
|
||||
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
|
||||
var needsAriaValuenow = !elem.attr('aria-valuenow');
|
||||
|
||||
if (needsAriaValuemin) {
|
||||
attr.$observe('min', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemin', newVal);
|
||||
});
|
||||
}
|
||||
if (needsAriaValuemax) {
|
||||
attr.$observe('max', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemax', newVal);
|
||||
});
|
||||
}
|
||||
if (needsAriaValuenow) {
|
||||
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
|
||||
elem.attr('aria-valuenow', newVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (needsTabIndex) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
|
||||
&& shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
|
||||
// ngModel.$error.required is undefined on custom controls
|
||||
attr.$observe('required', function() {
|
||||
elem.attr('aria-required', !!attr['required']);
|
||||
});
|
||||
}
|
||||
|
||||
if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
|
||||
scope.$watch(function ngAriaInvalidWatch() {
|
||||
return ngModel.$invalid;
|
||||
}, function ngAriaInvalidReaction(newVal) {
|
||||
elem.attr('aria-invalid', !!newVal);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
.directive('ngDisabled', ['$aria', function($aria) {
|
||||
return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
|
||||
}])
|
||||
.directive('ngMessages', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: '?ngMessages',
|
||||
link: function(scope, elem, attr, ngMessages) {
|
||||
if (!elem.attr('aria-live')) {
|
||||
elem.attr('aria-live', 'assertive');
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('ngClick',['$aria', '$parse', function($aria, $parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
compile: function(elem, attr) {
|
||||
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
|
||||
return function(scope, elem, attr) {
|
||||
|
||||
if (!isNodeOneOf(elem, nodeBlackList)) {
|
||||
|
||||
if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
|
||||
elem.attr('role', 'button');
|
||||
}
|
||||
|
||||
if ($aria.config('tabindex') && !elem.attr('tabindex')) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
|
||||
if ($aria.config('bindKeypress') && !attr.ngKeypress) {
|
||||
elem.on('keypress', function(event) {
|
||||
var keyCode = event.which || event.keyCode;
|
||||
if (keyCode === 32 || keyCode === 13) {
|
||||
scope.$apply(callback);
|
||||
}
|
||||
|
||||
function callback() {
|
||||
fn(scope, { $event: event });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}])
|
||||
.directive('ngDblclick', ['$aria', function($aria) {
|
||||
return function(scope, elem, attr) {
|
||||
if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
|
||||
elem.attr('tabindex', 0);
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
14
src/main/resources/static/vendor/angular/angular-aria.min.js
vendored
Normal file
14
src/main/resources/static/vendor/angular/angular-aria.min.js
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
AngularJS v1.5.6
|
||||
(c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
License: MIT
|
||||
*/
|
||||
(function(t,p){'use strict';var b="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),l=function(a,c){if(-1!==c.indexOf(a[0].nodeName))return!0};p.module("ngAria",["ng"]).provider("$aria",function(){function a(a,b,m,h){return function(d,f,e){var q=e.$normalize(b);!c[q]||l(f,m)||e[q]||d.$watch(e[a],function(a){a=h?!a:!!a;f.attr(b,a)})}}var c={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0,ariaValue:!0,tabindex:!0,bindKeypress:!0,bindRoleForClick:!0};
|
||||
this.config=function(a){c=p.extend(c,a)};this.$get=function(){return{config:function(a){return c[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",b,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked","aria-checked",b,!1)}]).directive("ngReadonly",
|
||||
["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",b,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",b,!1)}]).directive("ngModel",["$aria",function(a){function c(c,h,d,f){return a.config(h)&&!d.attr(c)&&(f||!l(d,b))}function g(a,c){return!c.attr("role")&&c.attr("type")===a&&"INPUT"!==c[0].nodeName}function k(a,c){var d=a.type,f=a.role;return"checkbox"===(d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"===
|
||||
f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(b,h){var d=k(h,b);return{pre:function(a,e,c,b){"checkbox"===d&&(b.$isEmpty=function(a){return!1===a})},post:function(f,e,b,n){function h(){return n.$modelValue}function k(a){e.attr("aria-checked",b.value==n.$viewValue)}function l(){e.attr("aria-checked",!n.$isEmpty(n.$viewValue))}var m=c("tabindex","tabindex",e,!1);switch(d){case "radio":case "checkbox":g(d,e)&&e.attr("role",
|
||||
d);c("aria-checked","ariaChecked",e,!1)&&f.$watch(h,"radio"===d?k:l);m&&e.attr("tabindex",0);break;case "range":g(d,e)&&e.attr("role","slider");if(a.config("ariaValue")){var p=!e.attr("aria-valuemin")&&(b.hasOwnProperty("min")||b.hasOwnProperty("ngMin")),r=!e.attr("aria-valuemax")&&(b.hasOwnProperty("max")||b.hasOwnProperty("ngMax")),s=!e.attr("aria-valuenow");p&&b.$observe("min",function(a){e.attr("aria-valuemin",a)});r&&b.$observe("max",function(a){e.attr("aria-valuemax",a)});s&&f.$watch(h,function(a){e.attr("aria-valuenow",
|
||||
a)})}m&&e.attr("tabindex",0)}!b.hasOwnProperty("ngRequired")&&n.$validators.required&&c("aria-required","ariaRequired",e,!1)&&b.$observe("required",function(){e.attr("aria-required",!!b.required)});c("aria-invalid","ariaInvalid",e,!0)&&f.$watch(function(){return n.$invalid},function(a){e.attr("aria-invalid",!!a)})}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",b,!1)}]).directive("ngMessages",function(){return{restrict:"A",require:"?ngMessages",
|
||||
link:function(a,b,g,k){b.attr("aria-live")||b.attr("aria-live","assertive")}}}).directive("ngClick",["$aria","$parse",function(a,c){return{restrict:"A",compile:function(g,k){var m=c(k.ngClick,null,!0);return function(c,d,f){if(!l(d,b)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeypress")&&!f.ngKeypress))d.on("keypress",function(a){function b(){m(c,{$event:a})}var d=a.which||a.keyCode;32!==d&&
|
||||
13!==d||c.$apply(b)})}}}}]).directive("ngDblclick",["$aria",function(a){return function(c,g,k){!a.config("tabindex")||g.attr("tabindex")||l(g,b)||g.attr("tabindex",0)}}])})(window,window.angular);
|
||||
//# sourceMappingURL=angular-aria.min.js.map
|
||||
8
src/main/resources/static/vendor/angular/angular-aria.min.js.map
vendored
Normal file
8
src/main/resources/static/vendor/angular/angular-aria.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
322
src/main/resources/static/vendor/angular/angular-cookies.js
vendored
Normal file
322
src/main/resources/static/vendor/angular/angular-cookies.js
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* @license AngularJS v1.5.6
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular) {'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngCookies
|
||||
* @description
|
||||
*
|
||||
* # ngCookies
|
||||
*
|
||||
* The `ngCookies` module provides a convenient wrapper for reading and writing browser cookies.
|
||||
*
|
||||
*
|
||||
* <div doc-module-components="ngCookies"></div>
|
||||
*
|
||||
* See {@link ngCookies.$cookies `$cookies`} for usage.
|
||||
*/
|
||||
|
||||
|
||||
angular.module('ngCookies', ['ng']).
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $cookiesProvider
|
||||
* @description
|
||||
* Use `$cookiesProvider` to change the default behavior of the {@link ngCookies.$cookies $cookies} service.
|
||||
* */
|
||||
provider('$cookies', [function $CookiesProvider() {
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $cookiesProvider#defaults
|
||||
* @description
|
||||
*
|
||||
* Object containing default options to pass when setting cookies.
|
||||
*
|
||||
* The object may have following properties:
|
||||
*
|
||||
* - **path** - `{string}` - The cookie will be available only for this path and its
|
||||
* sub-paths. By default, this is the URL that appears in your `<base>` tag.
|
||||
* - **domain** - `{string}` - The cookie will be available only for this domain and
|
||||
* its sub-domains. For security reasons the user agent will not accept the cookie
|
||||
* if the current domain is not a sub-domain of this domain or equal to it.
|
||||
* - **expires** - `{string|Date}` - String of the form "Wdy, DD Mon YYYY HH:MM:SS GMT"
|
||||
* or a Date object indicating the exact date/time this cookie will expire.
|
||||
* - **secure** - `{boolean}` - If `true`, then the cookie will only be available through a
|
||||
* secured connection.
|
||||
*
|
||||
* Note: By default, the address that appears in your `<base>` tag will be used as the path.
|
||||
* This is important so that cookies will be visible for all routes when html5mode is enabled.
|
||||
*
|
||||
**/
|
||||
var defaults = this.defaults = {};
|
||||
|
||||
function calcOptions(options) {
|
||||
return options ? angular.extend({}, defaults, options) : defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides read/write access to browser's cookies.
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* Up until Angular 1.3, `$cookies` exposed properties that represented the
|
||||
* current browser cookie values. In version 1.4, this behavior has changed, and
|
||||
* `$cookies` now provides a standard api of getters, setters etc.
|
||||
* </div>
|
||||
*
|
||||
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* angular.module('cookiesExample', ['ngCookies'])
|
||||
* .controller('ExampleController', ['$cookies', function($cookies) {
|
||||
* // Retrieving a cookie
|
||||
* var favoriteCookie = $cookies.get('myFavorite');
|
||||
* // Setting a cookie
|
||||
* $cookies.put('myFavorite', 'oatmeal');
|
||||
* }]);
|
||||
* ```
|
||||
*/
|
||||
this.$get = ['$$cookieReader', '$$cookieWriter', function($$cookieReader, $$cookieWriter) {
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#get
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {string} Raw cookie value.
|
||||
*/
|
||||
get: function(key) {
|
||||
return $$cookieReader()[key];
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#getObject
|
||||
*
|
||||
* @description
|
||||
* Returns the deserialized value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value.
|
||||
*/
|
||||
getObject: function(key) {
|
||||
var value = this.get(key);
|
||||
return value ? angular.fromJson(value) : value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#getAll
|
||||
*
|
||||
* @description
|
||||
* Returns a key value object with all the cookies
|
||||
*
|
||||
* @returns {Object} All cookies
|
||||
*/
|
||||
getAll: function() {
|
||||
return $$cookieReader();
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#put
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {string} value Raw value to be stored.
|
||||
* @param {Object=} options Options object.
|
||||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
|
||||
*/
|
||||
put: function(key, value, options) {
|
||||
$$cookieWriter(key, value, calcOptions(options));
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#putObject
|
||||
*
|
||||
* @description
|
||||
* Serializes and sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
* @param {Object=} options Options object.
|
||||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
|
||||
*/
|
||||
putObject: function(key, value, options) {
|
||||
this.put(key, angular.toJson(value), options);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookies#remove
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
* @param {Object=} options Options object.
|
||||
* See {@link ngCookies.$cookiesProvider#defaults $cookiesProvider.defaults}
|
||||
*/
|
||||
remove: function(key, options) {
|
||||
$$cookieWriter(key, undefined, calcOptions(options));
|
||||
}
|
||||
};
|
||||
}];
|
||||
}]);
|
||||
|
||||
angular.module('ngCookies').
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $cookieStore
|
||||
* @deprecated
|
||||
* @requires $cookies
|
||||
*
|
||||
* @description
|
||||
* Provides a key-value (string-object) storage, that is backed by session cookies.
|
||||
* Objects put or retrieved from this storage are automatically serialized or
|
||||
* deserialized by angular's toJson/fromJson.
|
||||
*
|
||||
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* **Note:** The $cookieStore service is **deprecated**.
|
||||
* Please use the {@link ngCookies.$cookies `$cookies`} service instead.
|
||||
* </div>
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```js
|
||||
* angular.module('cookieStoreExample', ['ngCookies'])
|
||||
* .controller('ExampleController', ['$cookieStore', function($cookieStore) {
|
||||
* // Put cookie
|
||||
* $cookieStore.put('myFavorite','oatmeal');
|
||||
* // Get cookie
|
||||
* var favoriteCookie = $cookieStore.get('myFavorite');
|
||||
* // Removing a cookie
|
||||
* $cookieStore.remove('myFavorite');
|
||||
* }]);
|
||||
* ```
|
||||
*/
|
||||
factory('$cookieStore', ['$cookies', function($cookies) {
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookieStore#get
|
||||
*
|
||||
* @description
|
||||
* Returns the value of given cookie key
|
||||
*
|
||||
* @param {string} key Id to use for lookup.
|
||||
* @returns {Object} Deserialized cookie value, undefined if the cookie does not exist.
|
||||
*/
|
||||
get: function(key) {
|
||||
return $cookies.getObject(key);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookieStore#put
|
||||
*
|
||||
* @description
|
||||
* Sets a value for given cookie key
|
||||
*
|
||||
* @param {string} key Id for the `value`.
|
||||
* @param {Object} value Value to be stored.
|
||||
*/
|
||||
put: function(key, value) {
|
||||
$cookies.putObject(key, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $cookieStore#remove
|
||||
*
|
||||
* @description
|
||||
* Remove given cookie
|
||||
*
|
||||
* @param {string} key Id of the key-value pair to delete.
|
||||
*/
|
||||
remove: function(key) {
|
||||
$cookies.remove(key);
|
||||
}
|
||||
};
|
||||
|
||||
}]);
|
||||
|
||||
/**
|
||||
* @name $$cookieWriter
|
||||
* @requires $document
|
||||
*
|
||||
* @description
|
||||
* This is a private service for writing cookies
|
||||
*
|
||||
* @param {string} name Cookie name
|
||||
* @param {string=} value Cookie value (if undefined, cookie will be deleted)
|
||||
* @param {Object=} options Object with options that need to be stored for the cookie.
|
||||
*/
|
||||
function $$CookieWriter($document, $log, $browser) {
|
||||
var cookiePath = $browser.baseHref();
|
||||
var rawDocument = $document[0];
|
||||
|
||||
function buildCookieString(name, value, options) {
|
||||
var path, expires;
|
||||
options = options || {};
|
||||
expires = options.expires;
|
||||
path = angular.isDefined(options.path) ? options.path : cookiePath;
|
||||
if (angular.isUndefined(value)) {
|
||||
expires = 'Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
value = '';
|
||||
}
|
||||
if (angular.isString(expires)) {
|
||||
expires = new Date(expires);
|
||||
}
|
||||
|
||||
var str = encodeURIComponent(name) + '=' + encodeURIComponent(value);
|
||||
str += path ? ';path=' + path : '';
|
||||
str += options.domain ? ';domain=' + options.domain : '';
|
||||
str += expires ? ';expires=' + expires.toUTCString() : '';
|
||||
str += options.secure ? ';secure' : '';
|
||||
|
||||
// per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
|
||||
// - 300 cookies
|
||||
// - 20 cookies per unique domain
|
||||
// - 4096 bytes per cookie
|
||||
var cookieLength = str.length + 1;
|
||||
if (cookieLength > 4096) {
|
||||
$log.warn("Cookie '" + name +
|
||||
"' possibly not set or overflowed because it was too large (" +
|
||||
cookieLength + " > 4096 bytes)!");
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
return function(name, value, options) {
|
||||
rawDocument.cookie = buildCookieString(name, value, options);
|
||||
};
|
||||
}
|
||||
|
||||
$$CookieWriter.$inject = ['$document', '$log', '$browser'];
|
||||
|
||||
angular.module('ngCookies').provider('$$cookieWriter', function $$CookieWriterProvider() {
|
||||
this.$get = $$CookieWriter;
|
||||
});
|
||||
|
||||
|
||||
})(window, window.angular);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user