Java tutorial
/* * Copyright 2007-2009 the original author or authors. * * 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.sinosoft.one.mvc.web.instruction; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.sinosoft.one.mvc.MvcConstants; import com.sinosoft.one.mvc.util.MvcPathUtil; import com.sinosoft.one.mvc.util.SpringUtils; import com.sinosoft.one.mvc.web.Invocation; import com.sinosoft.one.mvc.web.impl.thread.InvocationBean; import com.sinosoft.one.mvc.web.impl.view.ViewDispatcher; import com.sinosoft.one.mvc.web.impl.view.ViewDispatcherImpl; import com.sinosoft.one.mvc.web.impl.view.ViewPathCache; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; /** * {@link ViewInstruction} {@link Instruction}? {@link ViewResolver} * ? * * * */ public class ViewInstruction extends AbstractInstruction { protected static Log logger = LogFactory.getLog(ViewInstruction.class); public static final String MVC_INVOCATION = "mvcInvocation"; // ???(?????) private static Map<String, ViewPathCache> globalViewPathCaches = new ConcurrentHashMap<String, ViewPathCache>(); // ??????? private final String name; // applicationContext???viewResolver private String viewDispatcherName = "viewDispatcher"; public ViewInstruction(String name) { this.name = name; } @Override public void doRender(Invocation inv) throws Exception { String name = resolvePlaceHolder(this.name, inv); ViewDispatcher viewResolver = getViewDispatcher(inv); String viewPath = getViewPath((InvocationBean) inv, name); if (viewPath != null) { HttpServletRequest request = inv.getRequest(); HttpServletResponse response = inv.getResponse(); // View view = viewResolver.resolveViewName(inv, viewPath, request.getLocale()); if (!Thread.interrupted()) { inv.addModel(MVC_INVOCATION, inv); if (request.getAttribute(MvcConstants.IS_WINDOW_REQUEST) != null) { request.setAttribute(MvcConstants.WINDOW_REQUEST_URI, request.getContextPath() + viewPath); } view.render(inv.getModel().getAttributes(), request, response); } else { logger.info("interrupted"); } } } /** * * @param inv * @param viewName viewName (e.g: * index)?index.jsp? * ?/???template/default? * @return * @throws IOException */ private String getViewPath(InvocationBean inv, String viewName) throws IOException { if (logger.isDebugEnabled()) { logger.debug("resolving view name = '" + viewName + "'"); } // /??/views/? if (viewName.charAt(0) == '/' && !viewName.startsWith(MvcConstants.VIEWS_PATH_WITH_END_SEP)) { return viewName; } // String viewRelativePath; if (viewName.startsWith(MvcConstants.VIEWS_PATH_WITH_END_SEP)) { viewRelativePath = ""; viewName = viewName.substring(MvcConstants.VIEWS_PATH_WITH_END_SEP.length()); } else { viewRelativePath = inv.getViewModule().getRelativePath(); } ViewPathCache viewPathCache = globalViewPathCaches.get(viewRelativePath); if (viewPathCache == null) { String directoryPath = MvcConstants.VIEWS_PATH + viewRelativePath; File directFile = null; try { directFile = MvcPathUtil.getDirectoryFile(inv, directoryPath); } catch (Exception e) { throw new IOException(e.getLocalizedMessage()); } if (directFile == null || !directFile.exists()) { String msg = "404: view directory not found, you need to create it in your webapp:" + directoryPath; logger.error(msg); inv.getResponse().sendError(404, msg); return null; } viewPathCache = new ViewPathCache(viewRelativePath); globalViewPathCaches.put(viewRelativePath, viewPathCache); } // String viewPath; int queryStringIndex = viewName.indexOf('?'); if (queryStringIndex < 0) { viewPath = getViewPathFromCache(inv, viewPathCache, viewName); } else { viewPath = getViewPathFromCache(inv, viewPathCache, viewName.substring(0, queryStringIndex)) + viewName.substring(queryStringIndex); } if (viewPath != null) { if (logger.isDebugEnabled()) { logger.debug("found '" + viewPath + "' for viewName '" + viewName + "'"); } } return viewPath; } /** * * @param inv * @param viewPathCache * @param viewName * @return * @throws IOException */ private String getViewPathFromCache(InvocationBean inv, ViewPathCache viewPathCache, final String viewName) throws IOException { String viewPath = viewPathCache.getViewPath(viewName); if (viewPath != null) { return viewPath; } final boolean debugEnabled; if (debugEnabled = logger.isDebugEnabled()) { logger.debug("to find viewPath by viewName '" + viewName + "'"); } // final String notDirectoryViewName; File directoryFile; String directoryPath; // int viewNameIndex = viewName.lastIndexOf('/'); if (viewNameIndex > 0) { notDirectoryViewName = viewName.substring(viewNameIndex + 1); if (viewName.charAt(0) == '/') { directoryPath = viewName.substring(0, viewNameIndex); try { directoryFile = MvcPathUtil.getDirectoryFile(inv, directoryPath); } catch (Exception e) { throw new IOException(e.getLocalizedMessage()); } } else { directoryPath = viewPathCache.getDirectoryPath(); String subDirPath = viewName.substring(0, viewNameIndex); File tempHome = null; try { tempHome = new File(inv.getServletContext().getResource(directoryPath).toURI()); } catch (URISyntaxException e) { throw new IOException(e.getLocalizedMessage()); } if (!tempHome.exists()) { directoryFile = null; } else { directoryFile = searchDirectory(tempHome, subDirPath); if (directoryFile != null) { subDirPath = directoryFile.getPath().substring(tempHome.getPath().length()).replace('\\', '/'); directoryPath = directoryPath + subDirPath; } } } } else { directoryPath = viewPathCache.getDirectoryPath(); notDirectoryViewName = viewName; try { directoryFile = MvcPathUtil.getDirectoryFile(inv, directoryPath); } catch (Exception e) { throw new IOException(e.getLocalizedMessage()); } } if (directoryFile == null || !directoryFile.exists()) { logger.error("not found directoryPath '" + directoryPath + "' for directoryFile '" + directoryFile + "' of view named '" + viewName + "'"); inv.getResponse().sendError(404, "not found directoryPath '" + directoryPath + "'"); return null; } else { if (debugEnabled) { logger.debug("found directory " + directoryFile.getAbsolutePath()); } String viewFileName = searchViewFile(directoryFile, notDirectoryViewName, false); if (viewFileName == null) { viewFileName = searchViewFile(directoryFile, notDirectoryViewName, true); } if (viewFileName == null) { String msg = "not found view file '" + notDirectoryViewName + "' in " + directoryPath; if (logger.isWarnEnabled()) { logger.warn(msg); } inv.getResponse().sendError(404, msg); return null; } else { viewPath = directoryPath + "/" + viewFileName; viewPathCache.setViewPath(viewName, viewPath); return viewPath; } } } /** * ??????? * * @param tempHome * @param subDirPath * @return */ private File searchDirectory(File tempHome, String subDirPath) { // String[] subDirs = StringUtils.split(subDirPath, "/"); for (final String subDir : subDirs) { File file = new File(tempHome, subDir); if (!file.exists()) { String[] candidates = tempHome.list(new FilenameFilter() { public boolean accept(File dir, String name) { if (name.equalsIgnoreCase(subDir)) { return true; } return false; } }); if (candidates.length == 0) { tempHome = null; break; } else { tempHome = new File(tempHome, candidates[0]); } } else { tempHome = file; } } return tempHome; } /** * * @param fileNameToFind * @param directoryFile * @param ignoreCase * @return */ private String searchViewFile(File directoryFile, final String fileNameToFind, final boolean ignoreCase) { String[] viewFiles = directoryFile.list(new FilenameFilter() { public boolean accept(File dir, String fileName) { String _notDirectoryViewName = fileNameToFind; String _fileName = fileName; if (ignoreCase) { _fileName = fileName.toLowerCase(); _notDirectoryViewName = fileNameToFind.toLowerCase(); } // ? if (_fileName.startsWith(_notDirectoryViewName) && new File(dir, fileName).isFile()) { if (fileName.length() == fileNameToFind.length() && fileNameToFind.lastIndexOf('.') != -1) { return true; } if (fileName.length() > fileNameToFind.length() && fileName.charAt(fileNameToFind.length()) == '.') { return true; } } return false; } }); Arrays.sort(viewFiles); return viewFiles.length == 0 ? null : viewFiles[0]; } //------------------------------------------- protected ViewDispatcher getViewDispatcher(Invocation inv) { ViewDispatcher viewDispatcher = (ViewDispatcher) SpringUtils.getBean(inv.getApplicationContext(), viewDispatcherName); if (viewDispatcher == null) { viewDispatcher = registerViewDispatcher(inv.getApplicationContext()); } return viewDispatcher; } /** * {@link ViewDispatcher} * * @return */ protected ViewDispatcher registerViewDispatcher(WebApplicationContext applicationContext) { // ??????? synchronized (applicationContext) { if (SpringUtils.getBean(applicationContext, viewDispatcherName) == null) { GenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(ViewDispatcherImpl.class); ((BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory()) .registerBeanDefinition(viewDispatcherName, beanDefinition); if (logger.isDebugEnabled()) { logger.debug("registered bean definition:" + ViewDispatcherImpl.class.getName()); } } return (ViewDispatcher) SpringUtils.getBean(applicationContext, viewDispatcherName); } } }