public class WorkcenterResult implements WebConstant{
	
	private String resultCode; //200 success   500 server-error 600 business-exception
	
	private String resultMsg;  //detail
	
	private Object data;       //attachment
	
	private WorkcenterCodeEnum codeEnum;//error-code-msg enum

	private WorkcenterResult() {
		
	}
	
	private WorkcenterResult(String resultCode, String resultMsg, Object data, WorkcenterCodeEnum codeEnum) {
		this.resultCode = resultCode;
		this.resultMsg = resultMsg;
		this.data = data;
		this.codeEnum = codeEnum;
	}
	
	public String getResultCode() {
		return resultCode;
	}

	public String getResultMsg() {
		return resultMsg;
	}

	public Object getData() {
		return data;
	}

	public WorkcenterCodeEnum getCodeEnum() {
		return codeEnum;
	}

	public void setCodeEnum(WorkcenterCodeEnum codeEnum) {
		this.codeEnum = codeEnum;
	}
	
	public static Builder custom() {
		return new Builder();
	}
	
	public static class Builder {
		
		private String resultCode; //200  SUCCESS200    500server-error  ERROR500  600business-exception  BUSINESSEXCEPTION600
		
		private String resultMsg;  //detail
		
		private Object data;       //attachment
		
		private WorkcenterCodeEnum codeEnum;//error-code-msg enum
		
		Builder() {
			resultCode = SUCCESS200;
			resultMsg = "request success";
			data = "null";
			codeEnum = WorkcenterCodeEnum.valueOf(OK_SUCCESS);
		}
		
		public Builder setOK(WorkcenterCodeEnum codeEnum, Object data) {
			resultCode = SUCCESS200;
			resultMsg = "request success";
			this.data=data;
			this.codeEnum = codeEnum;
			return this;
		}
		
		public Builder setOK(WorkcenterCodeEnum codeEnum) {
			resultCode = SUCCESS200;
			resultMsg = "request success";
			this.data=null;
			this.codeEnum = codeEnum;
			return this;
		}
		
		public Builder setNO(WorkcenterCodeEnum codeEnum) {
			resultCode = EXCEPTION600;
			resultMsg = "business exception";
			this.data=null;
			this.codeEnum = codeEnum;
			return this;
		}
		
		public WorkcenterResult build() {
			return new WorkcenterResult(resultCode, resultMsg, data, codeEnum);
		}
		
	}
} public class WorkcenterResponseBodyJson implements ResponseBody , WebConstant{
	
	public final String returncode; //200 success   500 server-error 600 business-exception
	public final String returnmsg; 
	public final String returnmemo;//{SUCCESS/FAILURE}:{errorcode}:{errormsg}
	public final Object data;//attachment for data transfer to web-client 
	public final String operator;
	
	private WorkcenterResponseBodyJson() {
		returncode = "";
		returnmsg = "";
		returnmemo = "";
		operator = "";
		data = null;
	}
	
	private WorkcenterResponseBodyJson(String returncode, String returnmsg, String returnmemo, Object data, String operator) {
		this.returncode = returncode;
		this.returnmsg = returnmsg;
		this.returnmemo = returnmemo;
		this.operator = operator;
		this.data = data;
	}
	
	public static WorkcenterResponseBodyJson.Builder custom() {
		return new Builder();
	}
	
	public static class Builder {
		private String returncode;
		private String returnmsg;
		private String returnmemo;
		private String operator;
		private Object data;
		
		Builder() {
			super();
			this.returncode = "200";
			this.returnmsg = "request success";
			this.returnmemo = "SUCCESS:200000:request success";
			this.operator = "nothing";
			this.data = "attachment";
		}
		
		public WorkcenterResponseBodyJson.Builder setReturncode(String returncode) {
			this.returncode = returncode;
			return this;
		}

		public WorkcenterResponseBodyJson.Builder setReturnmsg(String returnmsg) {
			this.returnmsg = returnmsg;
			return this;
		}

		public WorkcenterResponseBodyJson.Builder setReturnmemo(String returnmemo) {
			this.returnmemo = returnmemo;
			return this;
		}
		
		public WorkcenterResponseBodyJson.Builder setOperator(String operator) {
			this.operator = operator;
			return this;
		}
		
		public Builder setAll(RuntimeException e) {
			this.returncode = "500";
			this.returnmsg = "server error";
			this.returnmemo = "FAILURE:10010000:RuntimeExceptionException" + COLON + e.getMessage();
			this.operator = null;
			this.data = null;
			return this;
		}
		
		public Builder setAll(Exception e) {
			this.returncode = "500";
			this.returnmsg = "server error";
			this.returnmemo = "FAILURE:10010000:Exception" + COLON + e.getMessage();
			this.operator = null;
			this.data = null;
			return this;
		}
		
		public Builder setAll(Throwable t) {
			this.returncode = "500";
			this.returnmsg = "server error";
			this.returnmemo = "FAILURE:10010000:Throwable" + COLON + t.getMessage();
			this.operator = null;
			this.data = null;
			return this;
		}
		
		public WorkcenterResponseBodyJson.Builder setAll(RuntimeException e, String operator) {
			this.returncode = "500";
			this.returnmsg = "server error";
			this.returnmemo = "FAILURE:10010000:RuntimeException" + COLON + e.getMessage();
			this.operator = operator;
			this.data = null;
			return this;
		}
		
		public WorkcenterResponseBodyJson.Builder setAll(WorkcenterResult result, String operator) {
			this.returncode = result.getResultCode();
			this.returnmsg = result.getResultMsg();
			this.returnmemo = result.getCodeEnum().getStatus() + COLON+result.getCodeEnum().getCode() + COLON+result.getCodeEnum().getMsg();
			this.operator = operator;
			this.data = result.getData();
			return this;
		}
		
		public WorkcenterResponseBodyJson.Builder setAll(WorkcenterResult result,  String operator, Object data) {
			this.returncode = result.getResultCode();
			this.returnmsg = result.getResultMsg();
			this.returnmemo = result.getCodeEnum().getStatus() + COLON+result.getCodeEnum().getCode() + COLON+result.getCodeEnum().getMsg();
			this.operator = operator;
			this.data = data;
			return this;
		}
		
		public WorkcenterResponseBodyJson.Builder setAll(String returncode, String returnmsg, String returnmemo, String operator , Object data){
			this.returncode = returncode;
			this.returnmsg = returnmsg;
			this.returnmemo = returnmemo;
			this.operator = operator;
			this.data = data;
			return this;
		}

		public WorkcenterResponseBodyJson build() {
			return new WorkcenterResponseBodyJson(returncode, returnmsg, returnmemo, data, operator);
		}

	}

	public String getReturncode() {
		return returncode;
	}

	public String getReturnmsg() {
		return returnmsg;
	}

	public String getReturnmemo() {
		return returnmemo;
	}
	
	public String getOperator() {
		return operator;
	}
	
	public Object getData() {
		return data;
	}
	
	@Override
	public String toString() {
		String json = JSON.toJSONString(this);
		return json;
	}
	
} public class StringUtil {
	
	public static void main(String[] args) {
		System.out.println(getRandom(6));
	}
	
	public static boolean isEmpty(String str){
		
		return ("".equals(str)||str == null) ;
	}
	
	public static String getRandom(int num) {
		String random = "";
		if(num<=0) 
			return random;
		for(int i=0;i<num;i++) {
			random += getRandom();
		}
		return random;
	}
	
	public static String getRandom(){
		return (int)(Math.random()*10)+"";
	}

	public static String getString(Map<String, Object> map, String key) {
		return getString(map, key, "");
	}
	
	public static String getString(Map<String, Object> map, String key, String defaultValue) {
		String value = (map.get(key)==null?"":map.get(key)).toString();
		value = StringUtils.isEmpty(value) ? "" : value;
		return value;
	}
	
	public static Date addMillis(long millis) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(calendar.getTimeInMillis() + millis);
		return calendar.getTime();

	}

	public static String getUUID() {
		String s = UUID.randomUUID().toString();
		// drop "-" 
		return s.replaceAll("-", "");
	}

	public static String getParameterByDefault(HttpServletRequest request, String key, String defaultValue) {
		String value = request.getParameter(key);
		if(isEmpty(value)) {
			return defaultValue;
		}
		return value;
	}
	
} public interface RedisService {

	void addRedisServer(String name, String host, int port, String password);
	
	void addKV(String serverName, int dbIndex, String dataType, String key, String[] values, double[] scores, String[] members, String[] fields);

	WorkcenterResult getKV(String serverName, int dbIndex, String dataType, String key);

	void delKV(String serverName, int dbIndex, String deleteKeys);

} public interface ViewService {

	Set<ZNode> getLeftTree();

	Set<RKey> getRedisKeys(Pagination pagination, String serverName, String dbIndex,
			String[] keyPrefixs, String queryKey, String queryValue);

	Set<ZNode> refresh();

	void changeRefreshMode(String mode);

	void changeShowType(String state);

	void refreshAllKeys();

} @SuppressWarnings("rawtypes")
public interface Constant {
	
	public static final Map<String, RedisTemplate> 					redisTemplatesMap 		= 	new HashMap<String, RedisTemplate>();
	public static final Map<String, CopyOnWriteArrayList<RKey>> 	redisKeysListMap 		= 	new HashMap<String, CopyOnWriteArrayList<RKey>>();
	public static final Map<RKey, Object> 							redisVMCache 			= 	new ConcurrentHashMap<RKey, Object>();
	public static final CopyOnWriteArrayList<ZNode> 				redisNavigateZtree 		= 	new CopyOnWriteArrayList<ZNode>();
	public static final CopyOnWriteArrayList<Map<String, Object>>	redisServerCache 		=   new CopyOnWriteArrayList<Map<String, Object>>();

	public static final int DEFAULT_ITEMS_PER_PAGE											= 10;
	public static final String DEFAULT_REDISKEY_SEPARATOR		 							= ":";
	public static final int REDIS_DEFAULT_DB_SIZE 											= 15;
	public static final String DEFAULT_SEPARATOR											= "_";
	public static final String UTF_8 														= "utf-8";
	
	/** redis properties key **/
	public static final String REDISPROPERTIES_SERVER_NUM_KEY								= 	"redis.server.num";
	public static final String REDISPROPERTIES_LANGUAGE_KEY									= 	"redis.language";
	
	public static final String REDISPROPERTIES_HOST_PROFIXKEY								= 	"redis.host.";
	public static final String REDISPROPERTIES_NAME_PROFIXKEY								= 	"redis.name.";
	public static final String REDISPROPERTIES_PORT_PROFIXKEY								= 	"redis.port.";
	public static final String REDISPROPERTIES_PASSWORD_PROFIXKEY							= 	"redis.password.";
	
	
	/** default **/
	//public static final String DEFAULT_REDISSERVERNAME 										= "default";
	public static final int DEFAULT_DBINDEX 												= 0;
	
	/** query key **/
	public static final String MIDDLE_KEY													= "middle";
	public static final String HEAD_KEY 													= "head";
	public static final String TAIL_KEY 													= "tail";
	public static final String EMPTY_STRING 												= "";
	
	/** operator for log **/
	public static final String GETKV 														= "GETKV";
} public class ConvertUtil {

	public static void convertByteToString(RedisConnection connection, Set<byte[]> keysSet, List<RKey> tempList) {
		StringRedisSerializer stringSerializer = new StringRedisSerializer(Charset.forName("UTF8"));
		for(byte[] byteArray: keysSet) {
			String converted = stringSerializer.deserialize(byteArray);
			DataType dateType = null;
			switch(RedisApplication.showType) {
			case show:
				dateType = connection.type(byteArray);
				break;
			case hide:
				dateType = DataType.NONE;
				break;
			default:
				dateType = connection.type(byteArray);
				break;
			}
			
			RKey rkey = new RKey(converted, dateType); 
			tempList.add(rkey);
		}
	}
	
	public static double[] convert2Double(String[] strings) {
		if(strings==null) return null;
		double[] doubleList = new double[strings.length];
		for(int i=0;i<strings.length;i++) {
			double d = Double.parseDouble(strings[i]);
			doubleList[i] = d;
		}
		return doubleList;
	}
} public class Pagination {

	private int maxentries;
	private int items_per_page;
	private int num_display_entries;
	private int current_page;
	private int num_edge_entries;
	private String link_to;
	private String prev_text;
	private String next_text;
	private String ellipse_text;
	private boolean prev_show_always;
	private boolean next_show_always;
	
	public Pagination() {
		items_per_page = 10;
		num_display_entries = 10;
		current_page = 0;
		num_edge_entries = 0;
		link_to = "#";
		prev_text = "Prev";
		next_text = "Next";
		ellipse_text = "...";
		prev_show_always = true;
		next_show_always = true;
	}
	
	public int getMaxentries() {
		return maxentries;
	}
	public void setMaxentries(int maxentries) {
		this.maxentries = maxentries;
	}
	public int getItems_per_page() {
		return items_per_page;
	}
	public void setItems_per_page(int items_per_page) {
		this.items_per_page = items_per_page;
	}
	public int getNum_display_entries() {
		return num_display_entries;
	}
	public void setNum_display_entries(int num_display_entries) {
		this.num_display_entries = num_display_entries;
	}
	public int getCurrent_page() {
		return current_page;
	}
	public void setCurrent_page(int current_page) {
		this.current_page = current_page;
	}
	public int getNum_edge_entries() {
		return num_edge_entries;
	}
	public void setNum_edge_entries(int num_edge_entries) {
		this.num_edge_entries = num_edge_entries;
	}
	public String getLink_to() {
		return link_to;
	}
	public void setLink_to(String link_to) {
		this.link_to = link_to;
	}
	public String getPrev_text() {
		return prev_text;
	}
	public void setPrev_text(String prev_text) {
		this.prev_text = prev_text;
	}
	public String getNext_text() {
		return next_text;
	}
	public void setNext_text(String next_text) {
		this.next_text = next_text;
	}
	public String getEllipse_text() {
		return ellipse_text;
	}
	public void setEllipse_text(String ellipse_text) {
		this.ellipse_text = ellipse_text;
	}
	public boolean isPrev_show_always() {
		return prev_show_always;
	}
	public void setPrev_show_always(boolean prev_show_always) {
		this.prev_show_always = prev_show_always;
	}
	public boolean isNext_show_always() {
		return next_show_always;
	}
	public void setNext_show_always(boolean next_show_always) {
		this.next_show_always = next_show_always;
	}

	public int getFromIndex() {
		int fromIndex = items_per_page * (current_page );
		return fromIndex;
	}

	public int getToIndex() {
		int toIndex = items_per_page * (current_page + 1);
		return toIndex;
	}

	public Pagination createLinkTo() {
		String link_to = (this.getLink_to()== null ||  this.getLink_to().equals("")) ? "&" :this.getLink_to();
		link_to += "&items_per_page=" + items_per_page;
		link_to += "&num_display_entries=" + num_display_entries;
		link_to += "&visit_page=__id__";
		link_to += "&num_edge_entries=" + num_edge_entries;
		link_to += "&prev_text=" + prev_text;
		link_to += "&next_text=" + next_text;
		link_to += "&ellipse_text=" + ellipse_text;
		link_to += "&prev_show_always=" + prev_show_always;
		link_to += "&next_show_always=" + next_show_always;
		this.setLink_to(link_to);
		return this;
	}
} public enum QueryEnum {

	middle("middle" , "like*"), 
	head("middle" , "head^"), 
	tail("middle" , "tail$"), 
	
	
	;
	
	private String queryKey;
	private String queryKeyCh;
	
	QueryEnum(String queryKey, String queryKeyCh) {
		this.queryKey = queryKey;
		this.queryKeyCh = queryKeyCh;
	}
	
	
	public String getQueryKey() {
		return queryKey;
	}
	public void setQueryKey(String queryKey) {
		this.queryKey = queryKey;
	}
	public String getQueryKeyCh() {
		return queryKeyCh;
	}
	public void setQueryKeyCh(String queryKeyCh) {
		this.queryKeyCh = queryKeyCh;
	}
	
} public class RKey implements Comparable {
	
	private String key;
	private DataType type;
	
	public RKey(String key, DataType dateType) {
		this.key = key;
		this.type = dateType;
	}
	public String getKey() {
		return key;
	}
	public void setKey(String key) {
		this.key = key;
	}
	public DataType getType() {
		return type;
	}
	public void setType(DataType type) {
		this.type = type;
	}
	
	@Override
	public int compareTo(Object o) {
		if(o == null) return 1;
		if(o instanceof RKey) {
			RKey rko = (RKey) o;
			return this.getKey().compareTo(rko.getKey());
		}
		if(o instanceof String) {
			String so = (String) o;
			return this.getKey().compareTo(so);
		}
		return 1;
	}
	@Override
	public boolean equals(Object o) {
		if(o == null) return false;
		if(o instanceof RKey) {
			RKey rko = (RKey) o;
			return this.getKey().equals(rko.getKey());
		}
		if(o instanceof String) {
			String so = (String) o;
			return this.getKey().equals(so);
		}
		return false;
	}
	
	public boolean contains(String str) {
		return this.getKey().contains(str);
	}
	
	public String[] split(String str) {
		return this.getKey().split(str);
	}
	public boolean startsWith(String str) {
		return this.getKey().startsWith(str);
	}
	public boolean endsWith(String str) {
		return this.getKey().endsWith(str);
	}
} public abstract class RedisApplication implements Constant{

	private static Log log = LogFactory.getLog(RedisApplication.class);
	
	public static volatile RefreshModeEnum refreshMode = RefreshModeEnum.manually;
	public static volatile ShowTypeEnum showType = ShowTypeEnum.show;
	public static String BASE_PATH = "/redis-admin";
	
	protected volatile Semaphore limitUpdate = new Semaphore(1);
	protected static final int LIMIT_TIME = 3; //unit : second
	
	public static ThreadLocal<Integer> redisConnectionDbIndex = new ThreadLocal<Integer>() {
		@Override
		protected Integer initialValue() {
			return 0;
		}
	};
	
	protected static ThreadLocal<Semaphore> updatePermition = new ThreadLocal<Semaphore>() {
		@Override
		protected Semaphore initialValue() {
			return null;
		}
	};
	
	protected static ThreadLocal<Long> startTime = new ThreadLocal<Long>() {
		protected Long initialValue() {
			return 0l;
		}
	};
	
	private Semaphore getSempahore() {
		startTime.set(System.currentTimeMillis());
		updatePermition.set(limitUpdate);
		return updatePermition.get();
		
	}
	protected boolean getUpdatePermition() {
		Semaphore sempahore = getSempahore();
		boolean permit = sempahore.tryAcquire(1);
		return permit;
	}
	
	protected void finishUpdate() {
		Semaphore semaphore = updatePermition.get();
		if(semaphore==null) {
			throw new ConcurrentException("semaphore==null");
		}
		final Semaphore fsemaphore = semaphore;
		new Thread(new Runnable() {
			
			Semaphore RSemaphore;
			{
				RSemaphore = fsemaphore;
			}
			
			@Override
			public void run() {
				long start = startTime.get();
				long now = System.currentTimeMillis();
				try {
					long needWait = start + LIMIT_TIME * 1000 - now;
					if(needWait > 0L) {
						Thread.sleep(needWait);
					}
				} catch (InterruptedException e) {
					log.warn("finishUpdate 's release semaphore thread had be interrupted");
				}
				RSemaphore.release(1);
				logCurrentTime("semaphore.release(1) finish");
			}
		}).start();
	}
	
	//this idea is not good
	/*protected void runUpdateLimit() {
		new Thread(new Runnable () {
			@Override
			public void run() {
				while(true) {
					try {
						Thread.sleep(LIMIT_TIME * 1000);
						limitUpdate = new Semaphore(1);
					} catch(InterruptedException e) {
						log.warn("runUpdateLimit 's new semaphore thread had be interrupted");
						break;
					}
				}
			}
		}).start();
	}*/
	
	protected void createRedisConnection(String name, String host, int port, String password) {
		JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
		connectionFactory.setHostName(host);
		connectionFactory.setPort(port);
		if(!StringUtils.isEmpty(password))
			connectionFactory.setPassword(password);
		connectionFactory.afterPropertiesSet();
		RedisTemplate redisTemplate = new MyStringRedisTemplate();
		redisTemplate.setConnectionFactory(connectionFactory);
		redisTemplate.afterPropertiesSet();
		RedisApplication.redisTemplatesMap.put(name, redisTemplate);
		
		Map<String, Object> redisServerMap = new HashMap<String, Object>();
		redisServerMap.put("name", name);
		redisServerMap.put("host", host);
		redisServerMap.put("port", port);
		redisServerMap.put("password", password);
		RedisApplication.redisServerCache.add(redisServerMap);
		
		initRedisKeysCache(redisTemplate, name);
		
		RedisZtreeUtil.initRedisNavigateZtree(name);
	}
	
	private void initRedisKeysCache(RedisTemplate redisTemplate, String name) {
		for(int i=0;i<=REDIS_DEFAULT_DB_SIZE;i++) {
			initRedisKeysCache(redisTemplate, name, i);
		}
	}
	
	
	protected void initRedisKeysCache(RedisTemplate redisTemplate, String serverName , int dbIndex) {
		RedisConnection connection = RedisConnectionUtils.getConnection(redisTemplate.getConnectionFactory());
		connection.select(dbIndex);
		Set<byte[]> keysSet = connection.keys("*".getBytes());
		connection.close();
		List<RKey> tempList = new ArrayList<RKey>();
		ConvertUtil.convertByteToString(connection, keysSet, tempList);
		Collections.sort(tempList);
		CopyOnWriteArrayList<RKey> redisKeysList = new CopyOnWriteArrayList<RKey>(tempList);
		if(redisKeysList.size()>0) {
			redisKeysListMap.put(serverName+DEFAULT_SEPARATOR+dbIndex, redisKeysList);
		}
	}
	
	protected static void logCurrentTime(String code) {
		log.debug("       code:"+code+"        当前时间:" + System.currentTimeMillis());
	}
} public class ZNode extends ZLeaf{
	
	private boolean open = true;
	
	private Set<ZLeaf> children;
	
	private boolean isParent;
	
	protected ZNode() {
		super();
	}
	
	public boolean isOpen() {
		return open;
	}
	public void setOpen(boolean open) {
		this.open = open;
	}
	public Set<ZLeaf> getChildren() {
		if(children==null) {
			children = new TreeSet<ZLeaf>();
		}
		return children;
	}
	public void setChildren(Set<ZLeaf> children) {
		this.children = children;
	}
	public boolean isParent() {
		return isParent;
	}
	public void setParent(boolean isParent) {
		this.isParent = isParent;
	}
	
	//add methods
	public static ZNode makeZNode(String name, Object attach) {
		ZNode node = new ZNode();
		node.name = name;
		node.attach = attach;
		return node;
	}
	
	public void addChildren(ZLeaf zLeaf) {
		if(children==null) {
			children = new TreeSet<ZLeaf>();
		}
		if(zLeaf == null) {
			return ;
		}
		children.add(zLeaf);
	}

	@Override
	public int hashCode() {
		return this.getName().hashCode();
	}
	
	@Override
	public String toString() {
		return this.getName();
	}
	
	@Override
	public boolean equals(Object o) {
		if(o == null) return false;
		if(o instanceof ZNode) {
			ZNode zno = (ZNode) o;
			return this.getName().equals(zno.getName());
		}
		if(o instanceof String) {
			return this.getName().equals(o);
		}
		return super.equals(o);
	}
	
	public void resetChildren() {
		children = new TreeSet<ZLeaf>();
	}
	
}
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
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;
import cn.workcenter.common.WorkcenterResult;
import cn.workcenter.common.response.WorkcenterResponseBodyJson;
import cn.workcenter.common.util.StringUtil;
import com.mauersu.service.RedisService;
import com.mauersu.service.ViewService;
import com.mauersu.util.Constant;
import com.mauersu.util.ConvertUtil;
import com.mauersu.util.Pagination;
import com.mauersu.util.QueryEnum;
import com.mauersu.util.RKey;
import com.mauersu.util.RedisApplication;
import com.mauersu.util.ztree.ZNode;
package com.mauersu.controller;






@Controller
@RequestMapping("/redis")
public class RedisController extends RedisApplication implements Constant{
	
	@Autowired
	private ViewService viewService;