/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.citrus.service.resource.impl;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.FileUtil.*;
import static com.alibaba.citrus.util.ObjectUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;
import static java.util.Collections.*;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import com.alibaba.citrus.service.resource.ResourceLoadingOption;
import com.alibaba.citrus.service.resource.ResourceLoadingService;
import com.alibaba.citrus.service.resource.ResourceMatchResult;
import com.alibaba.citrus.service.resource.ResourceNotFoundException;
import com.alibaba.citrus.util.Assert;
import com.alibaba.citrus.util.regex.MatchResultSubstitution;
import org.slf4j.Logger;
/**
* 查找和装载resource的逻辑。
*
* @author Michael Zhou
*/
abstract class AbstractResourceLoadingContext<R> implements ResourceMatchResult {
private final static Set<ResourceLoadingOption> EMPTY_OPTIONS = emptySet();
// 不变量
protected final Logger log;
protected final ResourceLoadingService parent;
private final String originalResourceName;
private final Set<ResourceLoadingOption> originalOptions;
private final ResourceMapping[] mappings;
private final BestResourcesMatcher resourcesMatcher;
// 变量
private List<ResourceMapping> visitedMappings;
protected String resourceName; // 当前正在匹配的resourceName
protected Set<ResourceLoadingOption> options; // 当前正在使用的options
protected ResourcePattern lastMatchedPattern; // 最近的匹配
protected MatchResultSubstitution lastSubstitution; // 和最近的匹配对应的替换工具
/** 创建一个context。 */
public AbstractResourceLoadingContext(String resourceName, Set<ResourceLoadingOption> options,
ResourceMapping[] mappings, ResourceLoadingService parent, Logger log) {
// 不变量
this.log = assertNotNull(log, "logger");
this.parent = parent;
this.originalResourceName = normalizeAbsolutePath(assertNotNull(trimToNull(resourceName), "resourceName"));
this.originalOptions = defaultIfNull(options, EMPTY_OPTIONS);
this.mappings = assertNotNull(mappings, "mappings");
this.resourcesMatcher = new BestResourcesMatcher();
// 变量
this.resourceName = originalResourceName;
this.options = originalOptions;
}
/** 实现<code>ResourceMatchResult.getResourceName()</code>。 */
public String getResourceName() {
return resourceName;
}
/** 实现<code>ResourceMatchResult.substitute()</code>。 */
public String substitute(String substitution) {
return resourceName.substring(0, lastSubstitution.getMatch().start())
+ lastSubstitution.substitute(substitution) + resourceName.substring(lastSubstitution.getMatch().end());
}
private static class SavedMatchResult {
String resourceName;
ResourcePattern lastMatchedPattern;
MatchResultSubstitution lastSubstitution;
ResourceMapping[] visitedMappings;
}
private LinkedList<SavedMatchResult> savedMatchResultStack = createLinkedList();
/** 实现<code>ResourceMatchResult.saveLastResult()</code>。 */
public void saveLastResult() {
SavedMatchResult savedMatchResult = new SavedMatchResult();
savedMatchResult.resourceName = resourceName;
savedMatchResult.lastMatchedPattern = lastMatchedPattern;
savedMatchResult.lastSubstitution = lastSubstitution;
savedMatchResult.visitedMappings = visitedMappings.toArray(new ResourceMapping[visitedMappings.size()]);
savedMatchResultStack.push(savedMatchResult);
}
/** 实现<code>ResourceMatchResult.restoreLastResult()</code>。 */
public void restoreLastResult() {
SavedMatchResult savedMatchResult = savedMatchResultStack.pop();
resourceName = savedMatchResult.resourceName;
lastMatchedPattern = savedMatchResult.lastMatchedPattern;
lastSubstitution = savedMatchResult.lastSubstitution;
visitedMappings.clear();
for (ResourceMapping mapping : savedMatchResult.visitedMappings) {
visitedMappings.add(mapping);
}
}
/** 寻找资源的真实逻辑,被filter chain调用,或被getResource直接调用(假如没有filter),或被list调用。 */
protected R doLoad(String newResourceName, Set<ResourceLoadingOption> newOptions) throws ResourceNotFoundException {
resourceName = newResourceName;
options = newOptions;
log.trace("Looking for resource: name={}", resourceName);
R resource = null;
ResourceNotFoundException chainingException = null;
if (visitedMappings == null) {
visitedMappings = createLinkedList();
}
if (findBestMatch()) {
// findBestMatch() 情况1. 找到alias,但没有找到最终的resource mapping
if (lastMatchedPattern instanceof ResourceAlias) {
if (parent != null) {
log.trace("Resource \"{}\" not found. Trying to find it in super ResourceLoadingService",
resourceName);
try {
resource = loadParentResource(resourceName, options);
} catch (ResourceNotFoundException e) {
// alias将改变resourceName,故保存异常作为caused by异常
chainingException = e;
}
}
} else {
// findBestMatch() 情况2, 3. 找到resource mapping
ResourceLoaderMapping mapping = (ResourceLoaderMapping) lastMatchedPattern;
resource = loadMappedResource(mapping, options);
if (resource == null) {
// 假如resourceName被改变,则保存异常作为caused by异常
if (!isEquals(resourceName, originalResourceName)) {
logResourceNotFound(resourceName);
chainingException = new ResourceNotFoundException(String.format(
"Could not find resource \"%s\"", resourceName));
}
}
}
}
// findBestMatch() 情况4. 什么也没找到
else {
if (parent != null) {
log.trace("Resource \"{}\" not found. " + "Trying to find it in super ResourceLoadingService",
resourceName);
// 直接抛出异常,因为送给parent的resourceName并未改变,没必要创建重复的异常信息
resource = loadParentResource(resourceName, options);
}
}
if (resource == null) {
logResourceNotFound(originalResourceName);
throw new ResourceNotFoundException(String.format("Could not find resource \"%s\"", originalResourceName),
chainingException);
}
log.debug("Found resource \"{}\": {}", originalResourceName, resource);
return resource;
}
/** 查找最佳匹配的<resource>或<resource-alias>。 */
private boolean findBestMatch() throws ResourceNotFoundException {
if (resourcesMatcher.matches(resourceName)) {
ResourceMapping resourceMapping = resourcesMatcher.bestMatchPettern;
lastMatchedPattern = resourceMapping;
lastSubstitution = new MatchResultSubstitution(resourcesMatcher.bestMatchResult);
// 递归查找resource alias,直到一个resource mapping被匹配为止
if (resourceMapping instanceof ResourceAlias) {
ResourceAlias alias = (ResourceAlias) resourceMapping;
// 设置alias替换后产生新的resourceName
String newResourceName = substitute(alias.getName());
if (log.isDebugEnabled()) {
log.debug("Resource \"{}\" matched resource-alias pattern: \"{}\". "
+ "Use a new resourceName: \"{}\"", new Object[] { resourceName, alias.getPatternName(),
newResourceName });
}
visitMapping(alias);
visitedMappings.add(alias);
resourceName = newResourceName;
// 递归匹配alias
findBestMatch();
// 情形1. findBestMatch()==false, 匹配了一个alias,但没找到可继续匹配项,则返回最后匹配的alias。
// 情形2. findBestMatch()==true, 匹配了一个alias,并递归找到了经过替换后的新匹配项,则返回最终的匹配。
// 无论哪种情型,都返回true
}
// 如果被匹配的是resource loader mapping,则返回之。
else if (resourceMapping instanceof ResourceLoaderMapping) {
ResourceLoaderMapping mapping = (ResourceLoaderMapping) resourceMapping;
log.debug("Resource \"{}\" matched pattern: \"{}\"", resourceName, mapping.getPatternName());
visitMapping(mapping);
visitedMappings.add(mapping);
// 情形3. 匹配了一个resource,返回true
}
return true;
}
// 情形4. 什么也没有匹配。
return false;
}
/** 回调函数:访问某个mapping。 */
protected abstract void visitMapping(ResourceMapping mapping);
/** 调用parent resource loading service取得资源。 */
protected abstract R loadParentResource(String resourceName, Set<ResourceLoadingOption> options)
throws ResourceNotFoundException;
/** 调用mapping取得资源。 */
protected abstract R loadMappedResource(ResourceLoaderMapping mapping, Set<ResourceLoadingOption> options);
/**
* 被<code>ResourceLoaderContext.getResource()</code>和
* <code>ResourceListerContext.list()</code>调用的通用方法。
*/
protected final R loadContextResource(String newResourceName, Set<ResourceLoadingOption> newOptions) {
assertTrue(!visitedMappings.isEmpty(), Assert.ExceptionType.ILLEGAL_STATE,
"getResource() can only be called within a ResourceLoader");
try {
// 如果当前resourceName和新的resourceName相同,则直接调用parent service
if (resourceName.equals(newResourceName)) {
if (parent == null) {
log.debug("No parent ResourceLoadingService exists for loading resource \"{}\"", newResourceName);
return null;
} else {
return loadParentResource(newResourceName, newOptions);
}
} else {
// 用新的名称装载资源
log.trace("Trying to find resource \"{}\" using a new resourceName: \"{}\"", resourceName,
newResourceName);
return doLoad(newResourceName, newOptions);
}
} catch (ResourceNotFoundException e) {
return null;
}
}
private void logResourceNotFound(String resourceName) {
log.trace("Resource \"{}\" not found", resourceName);
}
/**
* 找出最相关的匹配。
* <p>
* 算法:先按pattern相关度排序,再按匹配长度排序。
* </p>
*/
protected static abstract class BestMatcher<P extends ResourcePattern> {
protected String resourceName;
protected P bestMatchPettern;
protected MatchResult bestMatchResult;
private int bestMatchRelevancy;
private int bestMatchLength;
protected abstract void init();
protected abstract boolean accept(P pattern);
protected abstract P nextPattern();
public final boolean matches(String resourceName) {
// 因为此对象会被多次复用,所以使用前必须初始化参数
this.resourceName = assertNotNull(resourceName, "resourceName");
this.bestMatchPettern = null;
this.bestMatchResult = null;
this.bestMatchRelevancy = -1;
this.bestMatchLength = -1;
init();
for (P pattern = nextPattern(); pattern != null; pattern = nextPattern()) {
Matcher matcher = pattern.getPattern().matcher(resourceName);
if (matcher.find() && accept(pattern)) {
int relevancy = pattern.getRelevancy();
int length = matcher.group().length();
if (relevancy > bestMatchRelevancy || relevancy == bestMatchRelevancy && length > bestMatchLength) {
bestMatchPettern = pattern;
bestMatchResult = matcher;
bestMatchRelevancy = relevancy;
bestMatchLength = length;
}
}
}
return bestMatchLength >= 0;
}
}
/** 找出最匹配的<resource>或<resource-alias>。 */
private class BestResourcesMatcher extends BestMatcher<ResourceMapping> {
private int i;
@Override
protected void init() {
this.i = 0;
assertNotNull(visitedMappings, "visitedMappings");
}
@Override
protected ResourceMapping nextPattern() {
if (i < mappings.length) {
return mappings[i++];
} else {
return null;
}
}
@Override
protected boolean accept(ResourceMapping pattern) {
// visitedMappings为空,表示是第一个匹配,此时不适用internal mappings
if (visitedMappings.isEmpty() && pattern.isInternal()) {
return false;
}
// 除去已经匹配过的match,防止死循环
if (visitedMappings.contains(pattern)) {
return false;
}
return true;
}
}
}