/*
* Copyright (C) 2013-2017 NTT DATA Corporation
*
* 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 org.terasoluna.gfw.web.codelist;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.terasoluna.gfw.common.codelist.CodeList;
import org.terasoluna.gfw.common.codelist.i18n.I18nCodeList;
/**
* Interceptor class for setting codelist in attribute of {@link HttpServletRequest}
* <p>
* Default behavior is to set all the implementation beans of {@code CodeList} in the attribute of {@link HttpServletRequest}<br>
* In order to narrow down the target beans, pass the pattern (regular expression) corresponding to codelist ID of target beans
* <br>
* to {@link #setCodeListIdPattern(Pattern)} method.
* </p>
*/
public class CodeListInterceptor extends HandlerInterceptorAdapter
implements
ApplicationContextAware,
InitializingBean {
/**
* logger
*/
private static final Logger logger = LoggerFactory
.getLogger(CodeListInterceptor.class);
/**
* list of {@link CodeList}
*/
private Collection<CodeList> codeLists;
/**
* application context
*/
private ApplicationContext applicationContext;
/**
* Pattern of Codelist IDs (Bean IDs) of codelists which are target to be set to attribute of {@link HttpServletRequest}.
*/
private Pattern codeListIdPattern;
/**
* the default locale to fall back.<br>
* <p>
* this locale is used if the requested locale is not found in i18nCodeList.
* </p>
*/
private Locale fallbackTo = Locale.getDefault();
/**
* Sets codelist to the attribute of {@link HttpServletRequest}
* <p>
* Sets codelist to the attribute of {@link HttpServletRequest} before the execution of Controller.
* </p>
* @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse, java.lang.Object)
* @since 5.0.1
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (codeLists == null) {
return true;
}
Locale locale = RequestContextUtils.getLocale(request);
logger.debug("locale for I18nCodelist is '{}'.", locale);
for (CodeList codeList : codeLists) {
String attributeName = codeList.getCodeListId();
if (codeList instanceof I18nCodeList) {
I18nCodeList i18nCodeList = (I18nCodeList) codeList;
Map<String, String> codeListMap = getLocalizedCodeMap(
i18nCodeList, locale);
request.setAttribute(attributeName, codeListMap);
} else {
request.setAttribute(attributeName, codeList.asMap());
}
}
return true;
}
/**
* Returns the map of codelists which match to the specified locale.
* <p>
* If the map of codelists which match to the specified locale does not exist, returns the map of codelists which match<br>
* with fallback locale. If the map corresponding to fallback locale also does not exist, then log of WARN level is output<br>
* and an empty map is returned.<br>
* </p>
* @param i18nCodeList International Codelist
* @param requestLocale Locale of request
* @return Map of codelists which match to the specified locale
*/
protected Map<String, String> getLocalizedCodeMap(
I18nCodeList i18nCodeList, Locale requestLocale) {
Map<String, String> codeListMap = i18nCodeList.asMap(requestLocale);
if (codeListMap.isEmpty()
&& (fallbackTo != null && !fallbackTo.equals(requestLocale))) {
logger.debug("There is no mapping for '{}'. fall back to '{}'.",
requestLocale, fallbackTo);
codeListMap = i18nCodeList.asMap(fallbackTo);
if (codeListMap.isEmpty()) {
logger.warn("could not fall back to '{}'.", fallbackTo);
}
}
return codeListMap;
}
/**
* Extracts the {@code CodeList}s which are to be set to the attribute of {@link HttpServletRequest}
* <p>
* Among the Beans which implement {@code CodeList} interface, extract the Codelist IDs(Bean IDs) which match<br>
* with the regular expression specified in {@link #codeListIdPattern}.
* </p>
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
Assert.notNull(applicationContext, "applicationContext is null.");
if (this.codeListIdPattern == null) {
this.codeListIdPattern = Pattern.compile(".+");
}
Map<String, CodeList> definedCodeLists = BeanFactoryUtils
.beansOfTypeIncludingAncestors(applicationContext,
CodeList.class, false, false);
Map<String, CodeList> targetCodeLists = new HashMap<String, CodeList>();
for (CodeList codeList : definedCodeLists.values()) {
String codeListId = codeList.getCodeListId();
if (codeListId != null) {
Matcher codeListIdMatcher = this.codeListIdPattern
.matcher(codeListId);
if (codeListIdMatcher.matches()) {
targetCodeLists.put(codeListId, codeList);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("registered codeList : {}", targetCodeLists.keySet());
}
this.codeLists = Collections.unmodifiableCollection(targetCodeLists
.values());
}
/**
* Set the ApplicationContext.
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Sets Pattern (regular expression) of Codelist IDs (Bean IDs) of codelists which are target to be set to attribute of
* {@link HttpServletRequest}.
* <p>
* Default behavior is to include all beans
* </p>
* @param codeListIdPattern Pattern
*/
public void setCodeListIdPattern(Pattern codeListIdPattern) {
this.codeListIdPattern = codeListIdPattern;
}
/**
* Sets the default locale to fall back.
* <p>
* this locale is used if the requested locale is not found in i18nCodeList.
* </p>
* @param fallbackTo default locale used if the requested locale is not found in i18nCodeList
*/
public void setFallbackTo(Locale fallbackTo) {
this.fallbackTo = fallbackTo;
}
/**
* Returns the list of codelists which are to be set to attribute of {@link HttpServletRequest}
* @return list of codelists
*/
protected Collection<CodeList> getCodeLists() {
return codeLists;
}
}