提交 a2dc6adb 编写于 作者: F Frankie Wu

Merge pull request #63 from atellwu/abtest3

完成网络通讯,初步完成演示的demo
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dianping.cat</groupId>
<artifactId>abtest-sample</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>abtest-sample Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-core</artifactId>
<version>0.5.2.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.unidal.framework</groupId>
<artifactId>test-framework</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>abtest-sample</finalName>
</build>
</project>
package com.dianping.abtest.sample;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.dianping.cat.abtest.ABTest;
import com.dianping.cat.abtest.ABTestId;
import com.dianping.cat.abtest.ABTestManager;
public class ABTestSampleServlet extends HttpServlet {
private static final long serialVersionUID = -6472784609174835547L;
private ABTest m_abtest = ABTestManager.getTest(MyABTestId.CASE1);
@Override
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (m_abtest.isGroupA()) {
RequestDispatcher rd = getServletContext().getRequestDispatcher("/index2.jsp");
rd.forward(request, response);
// Cat.logMetric(...);
} else {
RequestDispatcher rd = getServletContext().getRequestDispatcher("/index.jsp");
rd.forward(request, response);
// Cat.logMetric(...);
}
}
public static enum MyABTestId implements ABTestId {
CASE1(0);
private int m_id;
private MyABTestId(int id) {
m_id = id;
}
@Override
public int getValue() {
return m_id;
}
}
}
package com.dianping.abtest.sample;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.unidal.helper.Splitters;
import org.unidal.lookup.util.StringUtils;
import com.dianping.cat.abtest.spi.ABTestContext;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class IPDistributionStrategy implements ABTestGroupStrategy {
public static final String ID = "ip-distribution";
public IPDistributionStrategy(){
System.out.println("new " + ID + " created");
}
@Override
public void apply(ABTestContext ctx) {
ABTestEntity entity = ctx.getEntity();
String config = entity.getGroupStrategyConfiguration();
List<String> ips = Splitters.by(',').trim().split(config);
HttpServletRequest req = ctx.getHttpServletRequest();
String address = getRemoteAddr(req);
for (String ip : ips) {
if (ip.equals(address)) {
ctx.setGroupName("A");
return;
}
}
}
public String getRemoteAddr(HttpServletRequest req) {
String ip = req.getHeader("X-Forwarded-For");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = req.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = req.getRemoteAddr();
if(ip.equals("127.0.0.1")){
ip = IPUtils.getFirstNoLoopbackIP4Address();
}
}
return ip;
}
}
package com.dianping.abtest.sample;
/**
* Project: puma-server
*
* File Created at 2012-7-24
* $Id$
*
* Copyright 2010 dianping.com.
* All rights reserved.
*
* This software is the confidential and proprietary information of
* Dianping Company. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with dianping.com.
*/
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
/**
* @author Leo Liang
*
*/
public final class IPUtils {
private IPUtils() {
}
/**
* 获取第一个no loop address
*
* @return first no loop address, or null if not exists
*/
public static String getFirstNoLoopbackIP4Address() {
Collection<String> allNoLoopbackIP4Addresses = getNoLoopbackIP4Addresses();
if (allNoLoopbackIP4Addresses.isEmpty()) {
return null;
}
return allNoLoopbackIP4Addresses.iterator().next();
}
public static Collection<String> getNoLoopbackIP4Addresses() {
Collection<String> noLoopbackIP4Addresses = new ArrayList<String>();
Collection<InetAddress> allInetAddresses = getAllHostAddress();
for (InetAddress address : allInetAddresses) {
if (!address.isLoopbackAddress() && !address.isSiteLocalAddress()
&& !Inet6Address.class.isInstance(address)) {
noLoopbackIP4Addresses.add(address.getHostAddress());
}
}
if (noLoopbackIP4Addresses.isEmpty()) {
// 降低过滤标准,将site local address纳入结果
for (InetAddress address : allInetAddresses) {
if (!address.isLoopbackAddress() && !Inet6Address.class.isInstance(address)) {
noLoopbackIP4Addresses.add(address.getHostAddress());
}
}
}
return noLoopbackIP4Addresses;
}
public static Collection<InetAddress> getAllHostAddress() {
try {
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
Collection<InetAddress> addresses = new ArrayList<InetAddress>();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
while (inetAddresses.hasMoreElements()) {
InetAddress inetAddress = inetAddresses.nextElement();
addresses.add(inetAddress);
}
}
return addresses;
} catch (SocketException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
\ No newline at end of file
Manifest-Version: 1.0
Class-Path:
Main-Class: com.dianping.cat.Server
<config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<domain id="Cat"/>
</config>
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="config" type="ConfigType" />
<xs:complexType name="ConfigType">
<xs:sequence>
<xs:element name="base-log-dir" type="xs:string" minOccurs="0" maxOccurs="1" />
<xs:element name="servers">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="server" type="ServerType" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element minOccurs="0" maxOccurs="unbounded" name="domain" type="DomainType" />
<xs:element name="bind" type="BindType" minOccurs="0" maxOccurs="1" />
<xs:element name="properties">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="property" type="PropertyType" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="mode" type="xs:string" use="required" />
</xs:complexType>
<xs:complexType name="ServerType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="ip" type="xs:string" />
<xs:attribute name="port" type="xs:int" default="2280" />
<xs:attribute name="enabled" type="xs:boolean" default="true" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="DomainType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="id" type="xs:string" />
<xs:attribute name="ip" type="xs:string" />
<xs:attribute name="enabled" type="xs:boolean" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="BindType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="ip" type="xs:string" />
<xs:attribute name="port" type="xs:string" default="2280" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="PropertyType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
<web-app>
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>com.dianping.cat.servlet.CatListener</listener-class>
</listener>
<filter>
<filter-name>cat-filter</filter-name>
<filter-class>com.dianping.cat.servlet.CatFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cat-filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<servlet>
<servlet-name>ABTestSampleServlet</servlet-name>
<servlet-class>com.dianping.abtest.sample.ABTestSampleServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ABTestSampleServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
<html>
<body>
<h2>Congratulation!</h2>
</body>
</html>
package com.dianping.abtest.sample;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.unidal.test.jetty.JettyServer;
@RunWith(JUnit4.class)
public class ABTestServer extends JettyServer {
public static void main(String[] args) throws Exception {
ABTestServer server = new ABTestServer();
System.setProperty("devMode", "true");
server.startServer();
server.startWebApp();
server.stopServer();
}
@Before
public void before() throws Exception {
System.setProperty("devMode", "true");
super.startServer();
}
@Override
protected String getContextPath() {
return "/abtest-sample";
}
@Override
protected int getServerPort() {
return 8081;
}
@Test
public void startWebApp() throws Exception {
// open the page in the default browser
display("/abtest-sample");
waitForAnyKey();
}
}
package com.dianping.abtest.sample;
import java.util.ArrayList;
import java.util.List;
import org.unidal.lookup.configuration.AbstractResourceConfigurator;
import org.unidal.lookup.configuration.Component;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class ABTestServerConfigurator extends AbstractResourceConfigurator {
@Override
public List<Component> defineComponents() {
List<Component> all = new ArrayList<Component>();
all.add(C(ABTestGroupStrategy.class, IPDistributionStrategy.ID, IPDistributionStrategy.class)
.is(PER_LOOKUP));
return all;
}
@Override
protected Class<?> getTestClass() {
return ABTestServer.class;
}
public static void main(String[] args) {
generatePlexusComponentsXmlFile(new ABTestServerConfigurator());
}
}
<plexus>
<components>
<component>
<role>com.dianping.cat.abtest.spi.ABTestGroupStrategy</role>
<role-hint>ip-distribution</role-hint>
<implementation>com.dianping.abtest.sample.IPDistributionStrategy</implementation>
<instantiation-strategy>per-lookup</instantiation-strategy>
</component>
</components>
</plexus>
......@@ -2,9 +2,11 @@ package com.dianping.cat.abtest;
import javax.servlet.http.HttpServletRequest;
import org.codehaus.plexus.PlexusContainer;
import org.unidal.lookup.ContainerLoader;
import com.dianping.cat.abtest.internal.DefaultABTest;
import com.dianping.cat.abtest.repository.ABTestEntityRepository;
import com.dianping.cat.abtest.spi.internal.ABTestContextManager;
public final class ABTestManager {
......@@ -22,7 +24,13 @@ public final class ABTestManager {
if (s_contextManager == null) {
try {
// it could be time-consuming due to load entities from the repository, i.e. database.
s_contextManager = ContainerLoader.getDefaultContainer().lookup(ABTestContextManager.class);
PlexusContainer container = ContainerLoader.getDefaultContainer();
s_contextManager = container.lookup(ABTestContextManager.class);
ABTestEntityRepository repository = container.lookup(ABTestEntityRepository.class);
repository.start();
} catch (Exception e) {
throw new RuntimeException("Error when initializing ABTestContextManager!", e);
}
......
package com.dianping.cat.abtest.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface UserID {
String[] value();
}
package com.dianping.cat.abtest.repository;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.dianping.cat.abtest.spi.ABTestEntity;
public interface ABTestEntityRepository {
public List<ABTestEntity> getEntities(Date from, Date to);
public Map<Integer, ABTestEntity> getEntities();
public void start();
}
package com.dianping.cat.abtest.repository;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.codehaus.plexus.util.StringUtils;
import org.unidal.helper.Splitters;
import org.unidal.lookup.ContainerHolder;
import org.unidal.lookup.annotation.Inject;
import org.unidal.socket.MessageInboundHandler;
import org.unidal.socket.udp.UdpSocket;
import com.dianping.cat.Cat;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Run;
import com.dianping.cat.abtest.model.transform.BaseVisitor;
import com.dianping.cat.abtest.model.transform.DefaultSaxParser;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
import com.dianping.cat.configuration.ClientConfigManager;
import com.dianping.cat.message.Heartbeat;
import com.dianping.cat.message.Message;
public class DefaultABTestEntityRepository implements ABTestEntityRepository, Initializable {
public class DefaultABTestEntityRepository extends ContainerHolder implements ABTestEntityRepository, Initializable,
LogEnabled, MessageInboundHandler<ProtocolMessage> {
@Inject
private ClientConfigManager m_configManager;
@Inject
private InetSocketAddress m_address = new InetSocketAddress("228.0.0.3", 2283);
@Inject
private Logger m_logger;
private UdpSocket m_socket;
private String m_domain;
private List<ABTestEntity> m_entities = new ArrayList<ABTestEntity>();
private Map<Integer, ABTestEntity> m_entities = new HashMap<Integer, ABTestEntity>();
private Map<String, ABTestGroupStrategy> m_strategies = new HashMap<String, ABTestGroupStrategy>();
@Override
public List<ABTestEntity> getEntities(Date from, Date to) {
return null;
public Map<Integer, ABTestEntity> getEntities() {
return m_entities;
}
@Override
public void initialize() throws InitializationException {
m_domain = m_configManager.getFirstDomain().getId();
}
// for test purpose
void setDomain(String domain) {
m_domain = domain;
}
@Override
public void start() {
m_socket = new UdpSocket();
m_socket.setName("ABTest");
m_socket.setCodec(new ProtocolMessageCodec());
m_socket.onMessage(new ProtocolHandler());
m_socket.onMessage(this);
m_socket.listenOn(m_address);
System.out.println("ABTestRepository init...");
ProtocolMessage hi = new ProtocolMessage();
hi.setName(ProtocolNames.HI);
hi.addHeader(ProtocolNames.HEARTBEAT, m_domain);
m_socket.send(hi);
}
public void setAddress(String address) {
......@@ -54,17 +89,94 @@ public class DefaultABTestEntityRepository implements ABTestEntityRepository, In
m_address = new InetSocketAddress(host, Integer.parseInt(port));
}
class ProtocolHandler implements MessageInboundHandler<ProtocolMessage> {
@Override
public void handle(ProtocolMessage message) {
String name = message.getName();
@Override
public void handle(ProtocolMessage message) {
String name = message.getName();
if (ProtocolNames.HEARTBEAT.equalsIgnoreCase(name)) {
Heartbeat h = Cat.newHeartbeat("abtest-heartbeat", message.getName()); // TODO .getFrom()
h.addData(message.toString());
h.setStatus(Message.SUCCESS);
h.complete();
String content = message.getContent();
if (StringUtils.isNotBlank(content)) {
try {
AbtestModel abtest = DefaultSaxParser.parse(content);
ABTestVisitor visitor = new ABTestVisitor(m_domain);
abtest.accept(visitor);
//switch the entities
m_entities = visitor.getEntities();
} catch (Exception e) {
Cat.logError(e);
}
}
} else {
m_logger.warn(String.format("Unknown command(%s) found in %s!", name, message));
}
}
if ("hi".equals(name)) {
} else if ("heartbeat".equals(name)) {
List<ABTestEntity> entities = new ArrayList<ABTestEntity>();
class ABTestVisitor extends BaseVisitor {
private String m_domain;
private Map<Integer, ABTestEntity> m_entities;
public ABTestVisitor(String domain) {
m_domain = domain;
m_entities = new HashMap<Integer, ABTestEntity>();
}
m_entities = entities;
@Override
public void visitCase(Case _case) {
for (Run run : _case.getRuns()) {
// filter abtest-entities by domain
if (run.getDomains() != null && run.getDomains().contains(m_domain)) {
ABTestEntity entity = new ABTestEntity(_case, run);
String strategyKey = String.format("%s+%s+%s", _case.getId(), entity.getGroupStrategyName(),
entity.getGroupStrategyConfiguration());
ABTestGroupStrategy strategy = m_strategies.get(strategyKey);
if (strategy != null) {
entity.setGroupStrategy(strategy);
} else {
try {
strategy = lookup(ABTestGroupStrategy.class, entity.getGroupStrategyName());
entity.setGroupStrategy(strategy);
m_strategies.put(strategyKey, strategy);
} catch (Exception e) {
Cat.logError(e);
ABTestEntity origin = DefaultABTestEntityRepository.this.m_entities.get(_case.getId());
if (origin != null) {
entity = origin;
} else {
entity.setDisabled(true);
}
}
}
m_entities.put(entity.getId(), entity);
}
}
}
public Map<Integer, ABTestEntity> getEntities() {
return m_entities;
}
}
@Override
public void enableLogging(Logger logger) {
m_logger = logger;
}
}
......@@ -6,6 +6,11 @@ import java.util.Map;
import org.unidal.socket.Message;
public class ProtocolMessage implements Message {
public static final String HELLO_NAME = "hello";
public static final String HEARTBEAT_NAME = "heartbeat";
private String m_name;
private String m_content;
......@@ -15,6 +20,10 @@ public class ProtocolMessage implements Message {
public String getContent() {
return m_content;
}
public void addHeader(String header, String value){
m_headers.put(header,value);
}
public Map<String, String> getHeaders() {
return m_headers;
......
package com.dianping.cat.abtest.repository;
public class ProtocolNames {
// protocol method
public static final String HI = "hi";
public static final String HEARTBEAT = "heartbeat";
// protocol headers
public static final String DOMAIN = "domain";
}
......@@ -6,39 +6,37 @@ import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Run;
public class ABTestEntity {
private Case m_case;
private int m_id;
private String m_name;
private String m_groupStrategyName;
private Run m_run;
private ABTestGroupStrategy m_groupStrategy;
public ABTestEntity() {
m_case = new Case();
m_run = new Run();
m_run.setDisabled(true);
}
public ABTestEntity(Case _case, Run run) {
m_case = _case;
m_id = _case.getId();
m_name = _case.getName();
m_groupStrategyName = _case.getGroupStrategy();
m_run = run;
}
public String getGroupStrategyName() {
return m_case.getGroupStrategy() != null ? m_case.getGroupStrategy() : null;
return m_groupStrategyName != null ? m_groupStrategyName : null;
}
public String getGroupStrategyConfiguration() {
return m_run.getGroupStrategyConfiguration() != null ? m_run.getGroupStrategyConfiguration() : null;
}
public int getId() {
return m_case.getId();
}
public String getName() {
return m_case.getName();
}
public Date getEndDate() {
return m_run.getEndDate();
}
......@@ -77,56 +75,75 @@ public class ABTestEntity {
m_run.setDisabled(disabled);
}
public void setGroupStrategyName(String groupStrategy) {
m_case.setGroupStrategy(groupStrategy);
public ABTestGroupStrategy getGroupStrategy() {
return m_groupStrategy;
}
public void setGroupStrategyConfiguration(String groupStrategyConfiguration) {
m_run.setGroupStrategyConfiguration(groupStrategyConfiguration);
public void setGroupStrategy(ABTestGroupStrategy groupStrategy) {
m_groupStrategy = groupStrategy;
}
public void setId(int id) {
m_case.setId(id);
public void setId(int id){
m_id = id;
}
public void setName(String name) {
m_case.setName(name);
public int getId() {
return m_id;
}
public ABTestGroupStrategy getGroupStrategy() {
return m_groupStrategy;
}
public String getName() {
return m_name;
}
public void setGroupStrategy(ABTestGroupStrategy groupStrategy) {
m_groupStrategy = groupStrategy;
public Run getRun() {
return m_run;
}
@Override
public String toString() {
return String.format("%s[id=%s, name=%s, groupStrategy=%s, configuation=%s]", getClass().getSimpleName(),
getId(), getName(), getGroupStrategyName(), getGroupStrategyConfiguration());
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((m_groupStrategyName == null) ? 0 : m_groupStrategyName.hashCode());
result = prime * result + m_id;
result = prime * result + ((m_name == null) ? 0 : m_name.hashCode());
result = prime * result + ((m_run == null) ? 0 : m_run.hashCode());
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getId();
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ABTestEntity other = (ABTestEntity) obj;
if (m_groupStrategyName == null) {
if (other.m_groupStrategyName != null)
return false;
} else if (!m_groupStrategyName.equals(other.m_groupStrategyName))
return false;
if (m_id != other.m_id)
return false;
if (m_name == null) {
if (other.m_name != null)
return false;
} else if (!m_name.equals(other.m_name))
return false;
if (m_run == null) {
if (other.m_run != null)
return false;
} else if (!m_run.equals(other.m_run))
return false;
return true;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ABTestEntity other = (ABTestEntity) obj;
if (getId() != other.getId())
return false;
return true;
}
public String toString() {
return "ABTestEntity [m_id=" + m_id + ", m_name=" + m_name + ", m_groupStrategyName=" + m_groupStrategyName
+ ", m_run=" + m_run +"]";
}
}
\ No newline at end of file
......@@ -6,6 +6,7 @@ import com.dianping.cat.abtest.ABTestId;
import com.dianping.cat.abtest.spi.ABTestEntity;
public interface ABTestEntityManager {
public ABTestEntity getEntity(ABTestId id);
public List<ABTestEntity> getEntityList();
......
......@@ -134,5 +134,4 @@ public class DefaultABTestContextManager extends ContainerHolder implements ABTe
m_req = req;
}
}
}
package com.dianping.cat.abtest.spi.internal;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.unidal.lookup.ContainerHolder;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.Cat;
import com.dianping.cat.abtest.ABTestId;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Run;
import com.dianping.cat.abtest.model.transform.BaseVisitor;
import com.dianping.cat.abtest.model.transform.DefaultSaxParser;
import com.dianping.cat.abtest.repository.ABTestEntityRepository;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class DefaultABTestEntityManager extends ContainerHolder implements ABTestEntityManager, Initializable {
private Map<Integer, ABTestEntity> m_entities = new HashMap<Integer, ABTestEntity>();
private List<ABTestEntity> m_entityList = new ArrayList<ABTestEntity>();
@Inject
private ABTestEntityRepository m_repository;
@Override
public ABTestEntity getEntity(ABTestId id) {
ABTestEntity entity = m_entities.get(id.getValue());
ABTestEntity entity = m_repository.getEntities().get(id.getValue());
if (entity == null) {
entity = new ABTestEntity();
entity.setId(id.getValue());
entity.setDisabled(true);
m_entities.put(id.getValue(), entity);
m_repository.getEntities().put(id.getValue(), entity);
}
return entity;
}
@Override
public List<ABTestEntity> getEntityList() {
return m_entityList;
List<ABTestEntity> entitiesList = new ArrayList<ABTestEntity>();
for (ABTestEntity entity : m_repository.getEntities().values()) {
entitiesList.add(entity);
}
return entitiesList;
}
@Override
public void initialize() throws InitializationException {
try {
InputStream in = getClass().getResourceAsStream("abtest.xml");
AbtestModel abtest = DefaultSaxParser.parse(in);
ABTestVisitor visitor = new ABTestVisitor(m_entities, m_entityList);
abtest.accept(visitor);
for (ABTestEntity entity : m_entityList) {
try {
ABTestGroupStrategy groupStrategy = lookup(ABTestGroupStrategy.class, entity.getGroupStrategyName());
entity.setGroupStrategy(groupStrategy);
} catch (Exception e) {
Cat.logError(e);
entity.setDisabled(true);
}
for (ABTestEntity entity : m_repository.getEntities().values()) {
try {
ABTestGroupStrategy groupStrategy = lookup(ABTestGroupStrategy.class, entity.getGroupStrategyName());
entity.setGroupStrategy(groupStrategy);
} catch (Exception e) {
Cat.logError(e);
entity.setDisabled(true);
}
} catch (Exception e) {
throw new InitializationException("Error when loading resource(abtest.xml)!", e);
}
}
static class ABTestVisitor extends BaseVisitor {
private Map<Integer, ABTestEntity> m_entities;
private List<ABTestEntity> m_entityList;
public ABTestVisitor(Map<Integer, ABTestEntity> entities, List<ABTestEntity> entityList) {
m_entities = entities;
m_entityList = entityList;
}
@Override
public void visitCase(Case _case) {
for (Run run : _case.getRuns()) {
ABTestEntity abTestEntity = new ABTestEntity(_case, run);
m_entities.put(abTestEntity.getId(), abTestEntity);
m_entityList.add(abTestEntity);
}
}
}
}
\ No newline at end of file
......@@ -22,12 +22,13 @@ class ABTestComponentConfigurator extends AbstractResourceConfigurator {
all.add(C(ABTestContextManager.class, DefaultABTestContextManager.class) //
.req(ABTestEntityManager.class));
all.add(C(ABTestEntityManager.class, DefaultABTestEntityManager.class));
all.add(C(ABTestEntityRepository.class, DefaultABTestEntityRepository.class) //
.req(ClientConfigManager.class) //
.config(E("address").value("228.0.0.3:2283")));
all.add(C(ABTestEntityManager.class, DefaultABTestEntityManager.class) //
.req(ABTestEntityRepository.class));
return all;
}
}
......@@ -312,12 +312,8 @@
</requirements>
</component>
<component>
<role>com.dianping.cat.abtest.spi.internal.ABTestEntityManager</role>
<implementation>com.dianping.cat.abtest.spi.internal.DefaultABTestEntityManager</implementation>
</component>
<component>
<role>com.dianping.cat.abtest.spi.internal.ABTestEntityRepository</role>
<implementation>com.dianping.cat.abtest.spi.internal.DefaultABTestEntityRepository</implementation>
<role>com.dianping.cat.abtest.repository.ABTestEntityRepository</role>
<implementation>com.dianping.cat.abtest.repository.DefaultABTestEntityRepository</implementation>
<configuration>
<address>228.0.0.3:2283</address>
</configuration>
......@@ -325,9 +321,14 @@
<requirement>
<role>com.dianping.cat.configuration.ClientConfigManager</role>
</requirement>
</requirements>
</component>
<component>
<role>com.dianping.cat.abtest.spi.internal.ABTestEntityManager</role>
<implementation>com.dianping.cat.abtest.spi.internal.DefaultABTestEntityManager</implementation>
<requirements>
<requirement>
<role>org.unidal.socket.udp.UdpSocket</role>
<role-hint>abtest</role-hint>
<role>com.dianping.cat.abtest.repository.ABTestEntityRepository</role>
</requirement>
</requirements>
</component>
......
package com.dianping.cat.abtest.repository;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import junit.framework.Assert;
import org.junit.Test;
import org.unidal.helper.Splitters;
import org.unidal.lookup.ComponentTestCase;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Run;
import com.dianping.cat.abtest.spi.ABTestEntity;
public class ABTestRepositoryTest extends ComponentTestCase {
private void checkHandler(String expected, String... keys) throws Exception {
DefaultABTestEntityRepository repository = (DefaultABTestEntityRepository) lookup(ABTestEntityRepository.class);
ProtocolMessage message = new ProtocolMessage();
repository.setDomain("domain2");
message.setName("heartbeat");
AbtestModel abtest = new AbtestModel();
for (String key : keys) {
List<String> parts = Splitters.by(':').trim().split(key);
int index = 0;
int id = Integer.parseInt(parts.get(index++));
String name = parts.get(index++);
List<String> domains = Splitters.by(',').noEmptyItem().trim().split(parts.get(index++));
Case _case = new Case(id).setName(name);
_case.getDomains().addAll(domains);
Run run = new Run(id);
run.getDomains().addAll(domains);
_case.addRun(run);
abtest.addCase(_case);
}
message.setContent(abtest.toString());
repository.handle(message);
StringBuilder sb = new StringBuilder(1024);
List<ABTestEntity> values = new ArrayList<ABTestEntity>(repository.getEntities().values());
boolean first = true;
Collections.sort(values, new Comparator<ABTestEntity>() {
@Override
public int compare(ABTestEntity o1, ABTestEntity o2) {
return o1.getName().compareTo(o1.getName());
}
});
for (ABTestEntity entity : values) {
if (first) {
first = false;
} else {
sb.append(',');
}
sb.append(entity.getName());
}
Assert.assertEquals(expected, sb.toString());
}
@Test
public void testHandler() throws Exception {
checkHandler("case2,case3", "1:case1:domain1", "2:case2:domain2", "3:case3:domain2,domain3");
}
}
......@@ -10,7 +10,7 @@ import org.unidal.lookup.ComponentTestCase;
import com.dianping.cat.abtest.spi.ABTestEntity;
public class DefaultABTestEntityManagerTest extends ComponentTestCase {
public class ABTestEntityManagerTest extends ComponentTestCase {
private static final String PATTERN = "yyyy-MM-dd hh:mm:ss";
......
<heartbeat>
<ip>192.168.0.1</ip> <!-- 看看能不能主动获得IP -->
<domain>tuangou</domain>
<whoami>client/server</whoami>
<entities>
<entity id="1001" name="Mock1" start-date="2013-04-10 17:00:00"
end-date="2013-12-30 18:00:00" disabled="false">
<description>This is a mock ABTest for test</description>
<group-strategy name="roundrobin">
<strategy-config>
<users>
<id>1001</id>
<id>1002</id>
<id></id>
</users>
<age>
<begin>20</begin>
<end>40</end>
</age>
<cookies>
<cookie>cityId</cookie>
<cookie>gender</cookie>
<cookie></cookie>
</cookies>
</strategy-config>
</group-strategy>
<domain>Cat</domain>
<domain>TuanGouWeb</domain>
</entity>
</entities>
<configuration><![CDATA[This is the configuration]]></configuration>
</heartbeat>
\ No newline at end of file
<strategy-config>
<users>
<id>1001</id>
<id>1002</id>
<id></id>
</users>
<age>
<begin>20</begin>
<end>40</end>
</age>
<cookies>
<cookie>cityId</cookie>
<cookie>gender</cookie>
<cookie></cookie>
</cookies>
</strategy-config>
\ No newline at end of file
......@@ -10,6 +10,11 @@
<name>CAT Home</name>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.unidal.framework</groupId>
<artifactId>foundation-service</artifactId>
<version>2.0.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-consumer</artifactId>
......
......@@ -21,14 +21,13 @@ import com.dianping.cat.system.alarm.threshold.listener.ServiceDataListener;
import com.dianping.cat.system.alarm.threshold.listener.ThresholdAlertListener;
import com.dianping.cat.system.event.EventListenerRegistry;
import com.dianping.cat.system.notify.ScheduledMailTask;
import com.dianping.cat.system.page.abtest.server.ABTestEntityServer;
public class CatHomeModule extends AbstractModule {
public static final String ID = "cat-home";
@Override
protected void execute(ModuleContext ctx) throws Exception {
// warm up IP seeker
// IPSeekerManager.initailize(new File(serverConfigManager.getStorageLocalBaseDir()));
ServerConfigManager serverConfigManager = ctx.lookup(ServerConfigManager.class);
ctx.lookup(MessageConsumer.class, "realtime");
......@@ -42,9 +41,17 @@ public class CatHomeModule extends AbstractModule {
Threads.forGroup("Cat").start(dailyTaskProducer);
}
executeAbtestModule(ctx);
executeAlarmModule(ctx);
}
private void executeAbtestModule(ModuleContext ctx) {
ABTestEntityServer server = ctx.lookup(ABTestEntityServer.class);
server.start();
}
private void executeAlarmModule(ModuleContext ctx) throws Exception {
ServerConfigManager serverConfigManager = ctx.lookup(ServerConfigManager.class);
......
......@@ -11,7 +11,7 @@ final class CatDatabaseConfigurator extends AbstractJdbcResourceConfigurator {
public List<Component> defineComponents() {
List<Component> all = new ArrayList<Component>();
all.add(defineJdbcDataSourceComponent("cat", "com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/cat", "root", "", "<![CDATA[useUnicode=true&autoReconnect=true]]>"));
all.add(defineJdbcDataSourceComponent("cat", "com.mysql.jdbc.Driver", "jdbc:mysql://192.168.7.43:3306/cat", "binlog", "binlog", "<![CDATA[useUnicode=true&autoReconnect=true]]>"));
defineSimpleTableProviderComponents(all, "cat", com.dianping.cat.home.dal.report._INDEX.getEntityClasses());
defineDaoComponents(all, com.dianping.cat.home.dal.report._INDEX.getDaoClasses());
......
......@@ -8,6 +8,9 @@ import org.unidal.lookup.configuration.Component;
import com.dianping.cat.configuration.ServerConfigManager;
import com.dianping.cat.hadoop.hdfs.HdfsMessageBucketManager;
import com.dianping.cat.home.dal.abtest.AbtestDao;
import com.dianping.cat.home.dal.abtest.AbtestRunDao;
import com.dianping.cat.home.dal.abtest.GroupStrategyDao;
import com.dianping.cat.message.spi.MessageCodec;
import com.dianping.cat.message.spi.MessageConsumer;
import com.dianping.cat.report.page.model.cross.CompositeCrossService;
......@@ -51,6 +54,8 @@ import com.dianping.cat.report.service.ReportService;
import com.dianping.cat.storage.BucketManager;
import com.dianping.cat.storage.dump.LocalMessageBucketManager;
import com.dianping.cat.storage.dump.MessageBucketManager;
import com.dianping.cat.system.page.abtest.server.ABTestEntityServer;
import com.dianping.cat.system.page.abtest.server.DefaultABTestEntityServer;
class ServiceComponentConfigurator extends AbstractResourceConfigurator {
@Override
......@@ -169,6 +174,11 @@ class ServiceComponentConfigurator extends AbstractResourceConfigurator {
.req(ServerConfigManager.class) //
.req(ModelService.class, new String[] { "message-historical", "logview-historical" }, "m_services"));
all.add(C(ABTestEntityServer.class, DefaultABTestEntityServer.class) //
.req(AbtestRunDao.class) //
.req(AbtestDao.class) //
.req(GroupStrategyDao.class));
return all;
}
}
......@@ -35,6 +35,7 @@ import com.dianping.cat.home.dal.abtest.GroupStrategyDao;
import com.dianping.cat.home.dal.abtest.GroupStrategyEntity;
import com.dianping.cat.system.SystemPage;
import com.dianping.cat.system.page.abtest.Model.AbtestDaoModel;
import com.dianping.cat.system.page.abtest.server.ABTestEntityServer;
public class Handler implements PageHandler<Context>, LogEnabled {
......@@ -59,6 +60,9 @@ public class Handler implements PageHandler<Context>, LogEnabled {
@Inject
private GroupStrategyDao m_groupStrategyDao;
@Inject
private ABTestEntityServer abTestEntityServer;
@Override
@PayloadMeta(Payload.class)
@InboundActionMeta(name = "abtest")
......@@ -94,16 +98,22 @@ public class Handler implements PageHandler<Context>, LogEnabled {
AbtestRun run = new AbtestRun();
run.setCreator(payload.getOwner());
run.setStartDate(payload.getStartDate());
run.setEndDate(payload.getEndDate());
run.setDomains(StringUtils.join(payload.getDomains(), ','));
run.setStrategyConfiguration(payload.getStrategyConfig());
run.setDisabled(false);
Date now = new Date();
run.setCreationDate(now);
run.setModifiedDate(now);
try {
m_abtestDao.insert(abtest);
run.setCaseId(abtest.getId());
m_abtestRunDao.insert(run);
abTestEntityServer.sendHeartbeat();
} catch (DalException e) {
m_logger.error("Error when saving abtest", e);
Cat.logError(e);
......@@ -117,12 +127,18 @@ public class Handler implements PageHandler<Context>, LogEnabled {
run.setId(payload.getId());
run.setKeyId(payload.getId());
run.setCreator(payload.getOwner());
run.setStartDate(payload.getStartDate());
run.setEndDate(payload.getEndDate());
run.setDomains(StringUtils.join(payload.getDomains(), ','));
run.setStrategyConfiguration(payload.getStrategyConfig());
Date now = new Date();
run.setModifiedDate(now);
// only update run info, do not update abtest meta-info
m_abtestRunDao.updateByPK(run, AbtestRunEntity.UPDATESET_ALLOWED_MODIFYPART);
abTestEntityServer.sendHeartbeat();
} catch (DalException e) {
m_logger.error("Error when updating abtest", e);
Cat.logError(e);
......@@ -156,6 +172,8 @@ public class Handler implements PageHandler<Context>, LogEnabled {
error.addArgument(id, "Abtest " + id + " has been already active!");
}
}
abTestEntityServer.sendHeartbeat();
} catch (NumberFormatException e) {
// do nothing
Cat.logError(e);
......
package com.dianping.cat.system.page.abtest.server;
public interface ABTestEntityServer {
void start();
void sendHeartbeat();
}
package com.dianping.cat.system.page.abtest.server;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.lang.StringUtils;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.unidal.dal.jdbc.DalException;
import org.unidal.helper.Splitters;
import org.unidal.lookup.annotation.Inject;
import org.unidal.socket.MessageInboundHandler;
import org.unidal.socket.udp.UdpSocket;
import com.dianping.cat.Cat;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Run;
import com.dianping.cat.abtest.model.transform.DefaultXmlBuilder;
import com.dianping.cat.abtest.repository.ProtocolMessage;
import com.dianping.cat.abtest.repository.ProtocolMessageCodec;
import com.dianping.cat.abtest.repository.ProtocolNames;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestDao;
import com.dianping.cat.home.dal.abtest.AbtestEntity;
import com.dianping.cat.home.dal.abtest.AbtestRun;
import com.dianping.cat.home.dal.abtest.AbtestRunDao;
import com.dianping.cat.home.dal.abtest.AbtestRunEntity;
import com.dianping.cat.home.dal.abtest.GroupStrategy;
import com.dianping.cat.home.dal.abtest.GroupStrategyDao;
import com.dianping.cat.home.dal.abtest.GroupStrategyEntity;
import com.dianping.cat.system.page.abtest.AbtestStatus;
/**
* send heartbeat(传输ABTestModel) every interval time.<br>
* receive hello from client, and reply heartbeat.<br>
* 一个case如果有多个run,只发当前最接近的一个run(不能多个run,cleint以caseId为key存储run的,所以client也只需一个run即可) server发送的run,是当前+最近即将生效的run(构建成ABTestModel来传输)。
*
* @author wukezhu
*/
public class DefaultABTestEntityServer implements ABTestEntityServer, Initializable, LogEnabled {
private static final String MULTICAST_ADDRESS = "228.0.0.3";
private static final int MULTICAST_PORT = 2283;
private static final int MINUTE_PERIOD = 60 * 1000;
private Logger m_logger;
@Inject
private AbtestRunDao m_abtestRunDao;
@Inject
private AbtestDao abtestDao;
@Inject
private GroupStrategyDao groupStrategyDao;
@Inject
private InetSocketAddress m_address = new InetSocketAddress(MULTICAST_ADDRESS, MULTICAST_PORT);
private UdpSocket m_socket;
private Timer m_timer = new Timer();
public DefaultABTestEntityServer() {
super();
}
@Override
public void initialize() throws InitializationException {
m_logger.info("ABTest Server initing.");
m_socket = new UdpSocket();
m_socket.setName("ABTest");
m_socket.setCodec(new ProtocolMessageCodec());
m_socket.onMessage(new ProtocolHandler());
m_logger.info("ABTest Server inited");
}
@Override
public void start() {
m_socket.listenOn(m_address);
// TODO 参考 com.dianping.cat.status.StatusUpdateTask 使用Thread代替TimerTask
// 启动heartbeatTask任务
HeartbeatTask heartbeatTask = new HeartbeatTask();
m_timer.schedule(heartbeatTask, 0, MINUTE_PERIOD);
}
public void setAddress(String address) {
List<String> parts = Splitters.by(':').trim().split(address);
int len = parts.size();
int index = 0;
String host = len > index ? parts.get(index++) : MULTICAST_ADDRESS;
int port = len > index ? Integer.parseInt(parts.get(index++)) : MULTICAST_PORT;
m_address = new InetSocketAddress(host, port);
}
private AbtestModel fetchAbtestModel() {
try {
AbtestModel abtestModel = new AbtestModel();
List<AbtestRun> abtestRuns = m_abtestRunDao.findAll(AbtestRunEntity.READSET_FULL);
if (abtestRuns != null) {
Date now = new Date();
for (AbtestRun abtestRun : abtestRuns) {
AbtestStatus status = AbtestStatus.calculateStatus(abtestRun, now);
if (status == AbtestStatus.READY || status == AbtestStatus.RUNNING) {
// fetch Case and GroupStrategy
int caseId = abtestRun.getCaseId();
Abtest entity = abtestDao.findByPK(caseId, AbtestEntity.READSET_FULL);
int gid = entity.getGroupStrategy();
GroupStrategy groupStrategy = groupStrategyDao.findByPK(gid, GroupStrategyEntity.READSET_FULL);
Case _case = transform(abtestRun, entity, groupStrategy);
abtestModel.addCase(_case);
}
}
}
return abtestModel;
} catch (DalException e) {
m_logger.error("Error when find all AbtestRun", e);
Cat.logError(e);
}
return null;
}
private Case transform(AbtestRun abtestRun, Abtest entity, GroupStrategy groupStrategy) throws DalException {
Case _case = new Case(entity.getId());
_case.setCreatedDate(entity.getCreationDate());
_case.setDescription(entity.getDescription());
_case.setGroupStrategy(groupStrategy.getName());
_case.setName(entity.getName());
_case.setOwner(entity.getOwner());
_case.setLastModifiedDate(entity.getModifiedDate());
for (String domain : StringUtils.split(entity.getDomains(), ',')) {
_case.addDomain(domain);
}
Run run = new Run(abtestRun.getId());
for (String domain : StringUtils.split(abtestRun.getDomains(), ',')) {
run.addDomain(domain);
}
run.setCreator(abtestRun.getCreator());
run.setDisabled(false);
run.setEndDate(abtestRun.getEndDate());
run.setGroupStrategyConfiguration(abtestRun.getStrategyConfiguration());
run.setStartDate(abtestRun.getStartDate());
_case.addRun(run);
return _case;
}
class HeartbeatTask extends TimerTask {
@Override
public void run() {
sendHeartbeat();
}
}
class ProtocolHandler implements MessageInboundHandler<ProtocolMessage> {
@Override
public void handle(ProtocolMessage message) {
String name = message.getName();
if (m_logger.isDebugEnabled()) {
m_logger.debug("Receive Message:" + message);
}
if (ProtocolNames.HI.equals(name)) {
sendHeartbeat();
}
}
}
@Override
public void enableLogging(Logger logger) {
m_logger = logger;
}
@Override
public void sendHeartbeat() {
AbtestModel abtestModel = fetchAbtestModel();
if (abtestModel != null) {
ProtocolMessage message = new ProtocolMessage();
message.setName(ProtocolNames.HEARTBEAT);
String content = new DefaultXmlBuilder().buildXml(abtestModel);
message.setContent(content);
m_socket.send(message);
m_logger.info("send heartbeat:" + message);
} else {
m_logger.info("send heartbeat: no abtest, so nothing to send.");
}
}
}
......@@ -4,7 +4,7 @@
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="name" field="name" value-type="String" length="20" nullable="false" />
<member name="ip" field="ip" value-type="String" length="20" />
<member name="domain" field="domain" value-type="String" length="50" nullable="false" />
<member name="domain" field="domain" value-type="String" length="200" nullable="false" />
<member name="period" field="period" value-type="Date" nullable="false" />
<member name="type" field="type" value-type="int" length="3" nullable="false" />
<member name="detail-content" field="detail_content" value-type="String" length="16777215" nullable="false" />
......@@ -50,11 +50,12 @@
<member name="domain" field="domain" value-type="String" length="50" nullable="false" />
<member name="period" field="period" value-type="Date" nullable="false" />
<member name="type" field="type" value-type="int" length="3" nullable="false" />
<member name="content" field="content" value-type="String" length="16777215" nullable="false" />
<member name="content" field="content" value-type="String" length="2147483647" nullable="false" />
<member name="creation-date" field="creation_date" value-type="Date" nullable="false" />
<var name="key-id" value-type="int" key-member="id" />
<primary-key name="PRIMARY" members="id" />
<index name="period" unique="true" members="period ASC, domain ASC, name ASC" />
<index name="period_column" members="period ASC" />
<readsets>
<readset name="FULL" all="true" />
</readsets>
......@@ -89,7 +90,7 @@
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="name" field="name" value-type="String" length="20" nullable="false" />
<member name="ip" field="ip" value-type="String" length="20" />
<member name="domain" field="domain" value-type="String" length="50" nullable="false" />
<member name="domain" field="domain" value-type="String" length="200" nullable="false" />
<member name="period" field="period" value-type="Date" nullable="false" />
<member name="type" field="type" value-type="int" length="3" nullable="false" />
<member name="detail-content" field="detail_content" value-type="String" length="16777215" nullable="false" />
......@@ -172,10 +173,10 @@
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="name" field="name" value-type="String" length="20" nullable="false" />
<member name="ip" field="ip" value-type="String" length="20" nullable="false" />
<member name="domain" field="domain" value-type="String" length="50" nullable="false" />
<member name="domain" field="domain" value-type="String" length="50" />
<member name="period" field="period" value-type="Date" nullable="false" />
<member name="type" field="type" value-type="int" length="3" nullable="false" />
<member name="content" field="content" value-type="String" length="16777215" nullable="false" />
<member name="content" field="content" value-type="String" length="2147483647" />
<member name="creation-date" field="creation_date" value-type="Date" nullable="false" />
<var name="key-id" value-type="int" key-member="id" />
<primary-key name="PRIMARY" members="id" />
......@@ -214,7 +215,7 @@
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="name" field="name" value-type="String" length="20" nullable="false" />
<member name="ip" field="ip" value-type="String" length="20" nullable="false" />
<member name="domain" field="domain" value-type="String" length="50" nullable="false" />
<member name="domain" field="domain" value-type="String" length="50" />
<member name="period" field="period" value-type="Date" nullable="false" />
<member name="type" field="type" value-type="int" length="3" nullable="false" />
<member name="content" field="content" value-type="String" length="16777215" nullable="false" />
......
......@@ -1141,6 +1141,21 @@
</requirement>
</requirements>
</component>
<component>
<role>com.dianping.cat.system.page.abtest.server.ABTestEntityServer</role>
<implementation>com.dianping.cat.system.page.abtest.server.DefaultABTestEntityServer</implementation>
<requirements>
<requirement>
<role>com.dianping.cat.home.dal.abtest.AbtestRunDao</role>
</requirement>
<requirement>
<role>com.dianping.cat.home.dal.abtest.AbtestDao</role>
</requirement>
<requirement>
<role>com.dianping.cat.home.dal.abtest.GroupStrategyDao</role>
</requirement>
</requirements>
</component>
<component>
<role>org.unidal.dal.jdbc.datasource.JdbcDataSourceConfigurationManager</role>
<implementation>org.unidal.dal.jdbc.datasource.JdbcDataSourceConfigurationManager</implementation>
......@@ -1160,9 +1175,9 @@
<statement-cache-size>1000</statement-cache-size>
<properties>
<driver>com.mysql.jdbc.Driver</driver>
<URL>jdbc:mysql://127.0.0.1:3306/cat</URL>
<user>root</user>
<password></password>
<URL>jdbc:mysql://192.168.7.43:3306/cat</URL>
<user>binlog</user>
<password>binlog</password>
<connectionProperties><![CDATA[useUnicode=true&autoReconnect=true]]></connectionProperties>
</properties>
</configuration>
......@@ -3171,6 +3186,9 @@
<requirement>
<role>com.dianping.cat.home.dal.abtest.GroupStrategyDao</role>
</requirement>
<requirement>
<role>com.dianping.cat.system.page.abtest.server.ABTestEntityServer</role>
</requirement>
</requirements>
</component>
<component>
......
......@@ -3,9 +3,9 @@
<jdbc package="com.dianping.cat.home.dal" name="cat">
<datasource>
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://127.0.0.1:3306/cat</url>
<user>root</user>
<password></password>
<url>jdbc:mysql://192.168.7.43:3306/cat</url>
<user>binlog</user>
<password>binlog</password>
<properties>useUnicode=true&amp;autoReconnect=true</properties>
</datasource>
<group name="report" package="com.dianping.cat.home.dal.report">
......
......@@ -158,11 +158,14 @@
<thead>
<tr class="centerth">
<th width="1%"></th>
<th width="6%">ID</th>
<th>Name</th>
<th>PV</th>
<th>ConversionRate</th>
<th style="display: none;" width="8%">Case's ID</th>
<th width="8%">Case's ID</th>
<th>Name</th>
<th>Domain</th>
<th>Start Time</th>
<th>End Time</th>
<th>Status</th>
<th>Creator</th>
<th>Created On</th>
</tr>
</thead>
......@@ -170,10 +173,12 @@
<c:forEach var="item" items="${model.reports}">
<tr class="middle center rowlink">
<td class="nolink" style="padding-bottom: 8px"><input type="checkbox"/></td>
<td><a href="abtest?op=report&id=${item.run.id}">${item.run.id}</a></td>
<td style="display: none;"><a href="abtest?op=report&id=${item.run.id}">${item.run.id}</a></td>
<td><span class="badge badge-success"><a href="abtest?op=report&id=${item.run.id}">${item.run.caseId}</a></span></td>
<td>${item.entity.name}</td>
<td>1000</td>
<td>10%</td>
<td>${item.run.domains}</td>
<td><fmt:formatDate pattern="yyyy-MM-dd hh:mm" value="${item.run.startDate}" /></td>
<td><fmt:formatDate pattern="yyyy-MM-dd hh:mm" value="${item.run.endDate}" /></td>
<td>
<c:choose>
<c:when test="${item.status.status eq 'created'}">
......@@ -203,7 +208,8 @@
</c:when>
</c:choose>
</td>
<td>Apr 1, 2013</td>
<td>${item.run.creator}</td>
<td><fmt:formatDate pattern="yyyy-MM-dd hh:mm" value="${item.run.startDate}" /></td>
</tr>
</c:forEach>
</tbody>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册