/**
* Project: dubbo.registry.console-2.1.0-SNAPSHOT
*
* File Created at Sep 5, 2011
* $Id: RestfuleUrlRewriter.java 181192 2012-06-21 05:05:47Z tony.chenl $
*
* Copyright 1999-2100 Alibaba.com Corporation Limited.
* All rights reserved.
*
* This software is the confidential and proprietary information of
* Alibaba 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 Alibaba.com.
*/
package com.alibaba.dubbo.governance.web.common.interceptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import com.alibaba.citrus.service.requestcontext.rewrite.RewriteSubstitutionContext;
import com.alibaba.citrus.service.requestcontext.rewrite.RewriteSubstitutionHandler;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
/**
* Restful URL Rewrite成 WebX的URL。
*
* @author ding.lid
*/
public class RestfuleUrlRewriter implements RewriteSubstitutionHandler {
private static final Logger logger = LoggerFactory.getLogger(RestfuleUrlRewriter.class);
private static final Map<String, String> pl2single;
private static final Set<String> appParameter;
static {
Map<String, String> map = new HashMap<String, String>();
map.put("services", "service");
map.put("users", "user");
map.put("addresses", "address");
map.put("applications", "application");
map.put("providers", "provider");
map.put("comsumers", "comsumer");
pl2single = Collections.unmodifiableMap(map);
Set<String> set = new HashSet<String>();
set.add("_path");
set.add("currentPage");
set.add("_method");
set.add("_type");
set.add("id");
appParameter = Collections.unmodifiableSet(set);
}
private final static String METHOD_KEY = "_method"; // show,
private final static String TYPE_KEY = "_type";
private final static String ID_KEY = "id";
private final static String PAGES_KEY = "currentPage";
private final static String PATH_KEY = "_path";
private final static Pattern SLASH_PATTERN = Pattern.compile("/+");
private final static Pattern NUM_PATTERN = Pattern.compile("\\d+");
private final static Pattern MULTI_NUM_PATTERN = Pattern.compile("[+\\d]+");
private final static Pattern PAGES_SPLIT_PATTERN = Pattern.compile("/+pages/+");
private final static Pattern PAGES_PATTERN = Pattern.compile(".*/+pages/+\\d+$");
public void postSubstitution(RewriteSubstitutionContext context) {
final String oldPath = context.getPath();
String path = oldPath;
while (path.startsWith("/")) {
path = path.substring(1);
}
Map<String, String> param = new HashMap<String, String>();
// 处理分页
if (PAGES_PATTERN.matcher(path).matches()) {
String[] page_path = PAGES_SPLIT_PATTERN.split(path);
path = page_path[0];
param.put(PATH_KEY, page_path[0]);
param.put(PAGES_KEY, page_path[1]);
} else {
param.put(PATH_KEY, path);
}
List<String> temp = Arrays.asList(SLASH_PATTERN.split(path));
//兼容2.0.x注册中心的shell风格url 如:http://root:hello1234@127.0.0.1:8080/status/dubbo.test.api.HelloService:1.1
if("status".equals(temp.get(0))&&temp.size()>1){
context.setPath("servicestatus");
return;
}
//兼容包含group的path
String[] split = temp.toArray(new String[temp.size()]);
if(temp.size()>2&&temp.contains("services")){
List<String> parts = new ArrayList<String>();
parts.addAll(temp);
for(int i = 0;i<temp.size();i++){
if ("services".equals(temp.get(i)) && i < (temp.size() - 1) && (!temp.get(i + 1).contains("."))&&(!temp.get(i + 1).matches("\\d+"))) {
String group = parts.get(i + 1);
String service = parts.get(i + 2);
parts.remove(i + 1);
parts.set(i+1, group + "/" + service);
break;
}
}
split = parts.toArray(new String[parts.size()]);
}
int index = split.length;
// module/action
if (split.length < 2) return;
// 最后一段不包含 '.',如 .htm .xsd .css etc
if (split[index - 1].contains(".")) {
return;
}
final String type;
// 偶数段
// module/k/v/type or module/k/v/type/id/method
if (index % 2 == 0) {
if (MULTI_NUM_PATTERN.matcher(split[index - 2]).matches()) {
// module/k/v/action/id/operate
if (index < 4) return;
param.put(METHOD_KEY, split[index - 1]);
param.put(ID_KEY, split[index - 2]);
type = split[index - 3];
param.put(TYPE_KEY, type);
index -= 3;
} else {
// module/k/v/type
type = split[index - 1];
param.put(TYPE_KEY, type);
--index;
}
}
// 奇数段
// module/k/v/type/method or module/k/v/type/id
else {
if (index < 3) return;
// module/k/v/type/id
if (NUM_PATTERN.matcher(split[index - 1]).matches()) {
param.put(ID_KEY, split[index - 1]);
}
// module/k/v/type/method
else {
param.put(METHOD_KEY, split[index - 1]);
}
type = split[index - 2];
param.put(TYPE_KEY, type);
index -= 2;
}
// 提取 KV对
for (int i = 1; i < index; i += 2) {
param.put(split[i], split[i + 1]);
}
String method = param.get(METHOD_KEY);
String defaultRedirect = null;
if(method == null || method.equals("index")){
defaultRedirect = oldPath;
}else{
defaultRedirect = oldPath.split("/" + method)[0];
}
String id = param.get(ID_KEY);
if(id != null){
int i = defaultRedirect.lastIndexOf("/");
defaultRedirect = defaultRedirect.substring(0,i);
}
context.getParameters().setString("defaultRedirect", defaultRedirect);
final String module = split[0];
context.setPath("/" + module + "/" + type + ".htm");
for (Map.Entry<String, String> entry : param.entrySet()) {
String key = entry.getKey();
if (pl2single.containsKey(key)) {
key = pl2single.get(key);
} else if (appParameter.contains(key)) {
// nothing
} else {
logger.info("key " + key + " is not pl noun!");
}
context.getParameters().setString(key, entry.getValue());
}
if (logger.isInfoEnabled()) {
logger.info("REWRITE restful uri " + oldPath + " to uri " + module + "/" + type + ".htm?" + param);
}
}
}