Java tutorial
/* * Copyright (c) 2000-2004 Netspective Communications LLC. All rights reserved. * * Netspective Communications LLC ("Netspective") permits redistribution, modification and use of this file in source * and binary form ("The Software") under the Netspective Source License ("NSL" or "The License"). The following * conditions are provided as a summary of the NSL but the NSL remains the canonical license and must be accepted * before using The Software. Any use of The Software indicates agreement with the NSL. * * 1. Each copy or derived work of The Software must preserve the copyright notice and this notice unmodified. * * 2. Redistribution of The Software is allowed in object code form only (as Java .class files or a .jar file * containing the .class files) and only as part of an application that uses The Software as part of its primary * functionality. No distribution of the package is allowed as part of a software development kit, other library, * or development tool without written consent of Netspective. Any modified form of The Software is bound by these * same restrictions. * * 3. Redistributions of The Software in any form must include an unmodified copy of The License, normally in a plain * ASCII text file unless otherwise agreed to, in writing, by Netspective. * * 4. The names "Netspective", "Axiom", "Commons", "Junxion", and "Sparx" are trademarks of Netspective and may not be * used to endorse or appear in products derived from The Software without written consent of Netspective. * * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, * ARE HEREBY DISCLAIMED. * * NETSPECTIVE AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A * RESULT OF USING OR DISTRIBUTING THE SOFTWARE. IN NO EVENT WILL NETSPECTIVE OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN * IF IT HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. */ package com.netspective.sparx.navigate; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.Vector; import java.util.zip.ZipFile; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.exception.NestableRuntimeException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tools.ant.BuildLogger; import org.apache.tools.ant.NoBannerLogger; import com.netspective.axiom.SqlManager; import com.netspective.commons.RuntimeEnvironment; import com.netspective.commons.RuntimeEnvironmentFlags; import com.netspective.commons.io.FileFind; import com.netspective.commons.io.MultipleUriAddressableFileLocators; import com.netspective.commons.io.UriAddressableFileLocator; import com.netspective.commons.io.UriAddressableInheritableFileResource; import com.netspective.commons.security.AuthenticatedUser; import com.netspective.commons.text.TextUtils; import com.netspective.commons.xdm.XdmComponentFactory; import com.netspective.sparx.Project; import com.netspective.sparx.ProjectComponent; import com.netspective.sparx.ProjectEvent; import com.netspective.sparx.ProjectLifecyleListener; import com.netspective.sparx.ProjectManager; import com.netspective.sparx.ant.AntProject; import com.netspective.sparx.navigate.client.AuthenticatedUserDelegatedServiceHandler; import com.netspective.sparx.navigate.client.ClientServiceRequestHandler; import com.netspective.sparx.navigate.client.SessionAttributeServiceHandler; import com.netspective.sparx.security.HttpLoginManager; import com.netspective.sparx.security.LoginDialogMode; import com.netspective.sparx.template.freemarker.FreeMarkerConfigurationAdapters; import com.netspective.sparx.theme.Theme; import com.netspective.sparx.theme.Themes; import com.netspective.sparx.util.HttpUtils; import com.netspective.sparx.value.BasicDbHttpServletValueContext; import freemarker.template.Configuration; public class NavigationControllerServlet extends HttpServlet implements RuntimeEnvironment, ProjectManager { private static final Log log = LogFactory.getLog(NavigationControllerServlet.class); private static final Set allControllerServlets = Collections.synchronizedSet(new HashSet()); private static final ThreadLocal SERVLET_CONTEXT = new ThreadLocal(); private static final ThreadLocal SERVLET = new ThreadLocal(); private static final ThreadLocal REQUEST = new ThreadLocal(); private static final ThreadLocal NAVIGATION_CONTEXT = new ThreadLocal(); public static final String CLIENT_SERVICE_REQUEST_HEADER_NAME = "Sparx-Http-Controller"; public static final String CLIENT_SERVICE_RESPONSE_HEADER_NAME = "Sparx-Http-Controller-Response"; public static final String REQATTRNAME_RENDER_START_TIME = NavigationControllerServlet.class.getName() + ".START_TIME"; public static final String PROPNAME_INIT_COUNT = "SERVLET_INITIALIZATION_COUNT"; public static final String REQPARAMNAME_COMMAND_ONLY = "command-only"; public static final String REQPARAMNAME_FORCE_PROJECT_RELOAD = "_force-reload"; public static Set getAllControllerServlets() { return allControllerServlets; } private NavigationControllerServletOptions servletOptions; private String projectSourceFileName; private Class projectComponentClass; private int lastProjectComponentRetrievedId; private Project project; private HttpLoginManager loginManager; private Theme theme; private NavigationTree navigationTree; private UriAddressableFileLocator resourceLocator; private RuntimeEnvironmentFlags runtimeEnvironmentFlags; private boolean cacheComponents; private String executionPropertiesFileName; private Properties executionProperties; private long initializationCount; private boolean initCountWritten; private Map staticPagesRendered = new HashMap(); private Configuration freeMarkerConfig; private Map clientServiceRequestHandlers = new HashMap(); public static ServletContext getThreadServletContext() { return (ServletContext) SERVLET_CONTEXT.get(); } public static NavigationControllerServlet getThreadServlet() { return (NavigationControllerServlet) SERVLET.get(); } public static HttpServletRequest getThreadRequest() { return (HttpServletRequest) REQUEST.get(); } public static NavigationContext getThreadNavigationContext() { return (NavigationContext) NAVIGATION_CONTEXT.get(); } protected static void setThreadServletContext(ServletContext servletContext) { SERVLET_CONTEXT.set(servletContext); } protected static void setThreadServlet(NavigationControllerServlet servlet) { SERVLET.set(servlet); } protected static void setThreadRequest(HttpServletRequest request) { REQUEST.set(request); } protected static void setThreadNavigationContext(NavigationContext navigationContext) { NAVIGATION_CONTEXT.set(navigationContext); } public void init(ServletConfig servletConfig) throws ServletException { allControllerServlets.add(this); super.init(servletConfig); servletOptions = constructServletOptions(servletConfig); if (servletOptions.isHelpRequested()) servletOptions.printHelp(); if (servletOptions.isDebugOptionsRequested()) System.out.println("** Servlet Options:\n" + servletOptions); loadExecutionProperties(servletConfig); if (getInitializationCount() == 1) initOnlyFirstExecution(servletConfig); initEachExecution(servletConfig); // if the init success is determined to be END_INIT we persist now, otherwise it will be done on first GET/POST if (servletOptions.getInitSuccessType().equals("END_INIT")) persistInitCount(); freeMarkerConfig = FreeMarkerConfigurationAdapters.getInstance() .constructWebAppConfiguration(getServletContext()); addClientServiceRequestHandler(new SessionAttributeServiceHandler()); addClientServiceRequestHandler(new AuthenticatedUserDelegatedServiceHandler()); } public void addClientServiceRequestHandler(ClientServiceRequestHandler handler) { clientServiceRequestHandlers.put(handler.getClientServiceRequestIdentifier(), handler); } protected NavigationControllerServletOptions constructServletOptions(ServletConfig servletConfig) { return new NavigationControllerServletOptions(servletConfig); } protected void loadExecutionProperties(ServletConfig servletConfig) throws ServletException { executionPropertiesFileName = checkWebInfAndGetRealPath( servletOptions.getServletExecutionPropertiesFileName()); executionProperties = new Properties(); try { executionProperties.load(new FileInputStream(new File(executionPropertiesFileName))); initializationCount = Long .valueOf(executionProperties.getProperty(getClass().getName() + '.' + PROPNAME_INIT_COUNT, "0")) .longValue(); } catch (FileNotFoundException e) { initializationCount = 0; } catch (IOException e) { throw new ServletException(e); } initializationCount++; log.debug("Initialization count is " + getInitializationCount()); } protected void persistInitCount() throws ServletException { executionProperties.setProperty(getClass().getName() + '.' + PROPNAME_INIT_COUNT, Long.toString(initializationCount)); saveExecutionProperties(); initCountWritten = true; } protected void saveExecutionProperties() throws ServletException { try { executionProperties.store(new FileOutputStream(new File(executionPropertiesFileName)), "Project execution properties"); } catch (IOException e) { throw new ServletException(e); } } protected void initRuntimeEnvironmentFlags(ServletConfig servletConfig) { String envFlagsText = servletOptions.getRuntimeEnvFlags(); try { Class envClass = Class.forName(servletOptions.getRuntimeEnvClassName()); runtimeEnvironmentFlags = (RuntimeEnvironmentFlags) envClass.newInstance(); } catch (Exception e) { log.error("Unable to instantiate environment flags using SPI -- creating statically instead", e); runtimeEnvironmentFlags = new RuntimeEnvironmentFlags(); } runtimeEnvironmentFlags.setValue(envFlagsText); setCacheComponents(!runtimeEnvironmentFlags .flagIsSet(RuntimeEnvironmentFlags.DEVELOPMENT | RuntimeEnvironmentFlags.FRAMEWORK_DEVELOPMENT)); } protected void executAntBuild(ServletConfig servletConfig, File buildFile, String target) throws ServletException { log.debug("Executing Ant build " + buildFile + " target " + target); org.apache.tools.ant.Project antProject = AntProject.getConfiguredProject(buildFile); antProject.setProperty("app.home", servletConfig.getServletContext().getRealPath("/")); antProject.setProperty("app.init-count", Long.toString(getInitializationCount())); Properties servletOptionsProps = servletOptions.setProperties(new Properties(), "app.servlet-options", false); for (Iterator i = servletOptionsProps.keySet().iterator(); i.hasNext();) { String propName = (String) i.next(); antProject.setProperty(propName, servletOptionsProps.getProperty(propName)); } ByteArrayOutputStream ostream = new ByteArrayOutputStream(); PrintStream pstream = new PrintStream(ostream); BuildLogger logger = new NoBannerLogger(); logger.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO); logger.setOutputPrintStream(pstream); logger.setErrorPrintStream(pstream); PrintStream saveOut = System.out; PrintStream saveErr = System.err; System.setOut(pstream); System.setErr(pstream); antProject.addBuildListener(logger); Exception exceptionThrown = null; try { Vector targets = new Vector(); if (target != null) { String[] targetNames = TextUtils.getInstance().split(target, ",", true); for (int i = 0; i < targetNames.length; i++) targets.add(targetNames[i]); } else targets.add(antProject.getDefaultTarget()); antProject.executeTargets(targets); } catch (Exception e) { exceptionThrown = e; } if (exceptionThrown != null) { log.error(ostream.toString()); log.error("Error running ant build file " + buildFile + " target " + target, exceptionThrown); } else log.debug(ostream.toString()); int extnPos = buildFile.getName().lastIndexOf('.'); String nameNoExtn = extnPos != -1 ? buildFile.getName().substring(0, extnPos) : buildFile.getName(); File logFile = servletOptions.getInitUsingAntLogFile() != null ? new File(servletOptions.getInitUsingAntLogFile()) : new File(buildFile.getParentFile(), nameNoExtn + ".log"); FileOutputStream fos = null; PrintWriter logWriter = null; try { fos = new FileOutputStream(logFile.getAbsolutePath(), logFile.exists()); logWriter = new PrintWriter(fos); logWriter.println("-----------------------------------------------------------------------------"); logWriter.println("Started build at " + SimpleDateFormat.getDateTimeInstance().format(new Date()) + " in Servlet " + getServletName() + " (Context " + getServletContext().getServletContextName() + ", BuildFile " + buildFile.getAbsolutePath() + ")"); logWriter.write(ostream.toString()); if (exceptionThrown != null) logWriter.write(TextUtils.getInstance().getStackTrace(exceptionThrown)); logWriter.println("Ended build at " + SimpleDateFormat.getDateTimeInstance().format(new Date()) + " in Servlet " + getServletName() + " (Context " + getServletContext().getServletContextName() + ", BuildFile " + buildFile.getAbsolutePath() + ")"); } catch (IOException e) { throw new ServletException(e); } finally { logWriter.close(); try { fos.close(); } catch (IOException e) { throw new ServletException(e); } } System.setOut(saveOut); System.setErr(saveErr); } protected void initUsingAnt(ServletConfig servletConfig, String optionText) throws ServletException { String buildFileName = optionText; String target = null; int targetNameDelimPos = optionText.lastIndexOf(':'); if (targetNameDelimPos > 0) { buildFileName = optionText.substring(0, targetNameDelimPos); target = optionText.substring(targetNameDelimPos + 1); } executAntBuild(servletConfig, new File(checkWebInfAndGetRealPath(buildFileName)), target); } /** * Called when the servlet is intialized for the very first time in this servlet container. The init count is * stored in a properties file. */ protected void initOnlyFirstExecution(ServletConfig servletConfig) throws ServletException { if (servletOptions.getInitFirstTimeUsingAnt() != null) initUsingAnt(servletConfig, servletOptions.getInitFirstTimeUsingAnt()); } protected void initEachExecution(ServletConfig servletConfig) throws ServletException { if (servletOptions.getInitUsingAnt() != null) initUsingAnt(servletConfig, servletOptions.getInitUsingAnt()); try { projectComponentClass = Class.forName(servletOptions.getProjectComponentClassName()); } catch (ClassNotFoundException e) { log.error("Unable to find class for ProjectComponent instance.", e); throw new ServletException(e); } projectSourceFileName = checkWebInfAndGetRealPath(servletOptions.getProjectFileName()); File xdmSourceFile = new File(projectSourceFileName); if (!xdmSourceFile.exists()) throw new ServletException("Sparx XDM source file '" + xdmSourceFile.getAbsolutePath() + "' does not exist. Please " + "correct the servlet-param called '" + NavigationControllerServletOptions.INITPARAMNAME_SERVLET_OPTIONS + "' in your WEB-INF/web.xml file."); initRuntimeEnvironmentFlags(servletConfig); if (isCacheComponents()) { // go ahead and grab all the components now -- so that we don't have to synchronize calls later getProject(); getLoginManager(); getNavigationTree(); } } public Configuration getFreeMarkerConfiguration() { return freeMarkerConfig; } /** * Initializes the web resource locators to the following: * - APP_ROOT/resources/sparx (will only exist if user is overriding any defaults) * - APP_ROOT/sparx (will exist in ITE mode when sparx directory is inside application) * - [CLASS_PATH]/Sparx/resources (only useful during development in SDE, not production since it won't be found) * TODO: this method is _not_ thread-safe because two requests could call the method at the same time FIX IT */ protected UriAddressableFileLocator getResourceLocator(HttpServletRequest request) throws ServletException { if (resourceLocator != null) return resourceLocator; ServletContext servletContext = getServletContext(); try { String[] webAppLocations = TextUtils.getInstance().split(servletOptions.getSparxResourceLocators(), ",", false); List locators = new ArrayList(); for (int i = 0; i < webAppLocations.length; i++) { String webAppRelativePath = webAppLocations[i]; File webAppPhysicalDir = new File(servletContext.getRealPath(webAppRelativePath)); if (webAppPhysicalDir.exists() && webAppPhysicalDir.isDirectory()) locators.add(new UriAddressableInheritableFileResource( request.getContextPath() + webAppRelativePath, webAppPhysicalDir, isCacheComponents())); } // this will only match the SDE development environment FileFind.FileFindResults ffResults = FileFind.findInClasspath("Sparx/resources", FileFind.FINDINPATHFLAG_DEFAULT); if (ffResults.isFileFound() && ffResults.getFoundFile().isDirectory()) locators.add(new UriAddressableInheritableFileResource(request.getContextPath() + "/sparx", ffResults.getFoundFile(), isCacheComponents())); if (log.isDebugEnabled()) { for (int i = 0; i < locators.size(); i++) log.debug("Registered web resources locator " + locators.get(i)); } if (locators.size() == 0) System.err.println("Unable to register any web resource locators (" + TextUtils.getInstance().join(webAppLocations, ", ") + " were not found). Please use the SparxResourcesServlet for serving Sparx resources."); // this will allow serving up files directly from the JAR (assuming the SparxResourcesServlet is available for serving those files) final File jarFile = new File(servletContext.getRealPath("WEB-INF/lib/netspective-sparx.jar")); if (jarFile.exists()) locators.add(new UriAddressableSparxJarResourceLocator(request.getContextPath() + "/sparx", new ZipFile(jarFile))); resourceLocator = new MultipleUriAddressableFileLocators( (UriAddressableFileLocator[]) locators.toArray(new UriAddressableFileLocator[locators.size()]), isCacheComponents()); return resourceLocator; } catch (IOException e) { log.error("error initializing resource locator", e); throw new ServletException(e); } } public String checkWebInfAndGetRealPath(String path) { if (path.startsWith("/WEB-INF")) return getServletContext().getRealPath(path); if (path.startsWith("WEB-INF")) return getServletContext().getRealPath("/" + path); return path; } public NavigationControllerServletOptions getServletOptions() { return servletOptions; } public String getExecutionPropertiesFileName() { return executionPropertiesFileName; } public Properties getExecutionProperties() { return executionProperties; } public long getInitializationCount() { return initializationCount; } public RuntimeEnvironmentFlags getRuntimeEnvironmentFlags() { return runtimeEnvironmentFlags; } public boolean isCacheComponents() { return cacheComponents; } protected void setCacheComponents(boolean cacheComponents) { this.cacheComponents = cacheComponents; } public boolean isSecure() throws ServletException { return getLoginManager() != null; } public Theme getTheme(HttpServletRequest request) throws ServletException { if (theme == null || !isCacheComponents()) { String themeName = servletOptions.getThemeName(); Themes themes = getProject().getThemes(); theme = themeName != null ? themes.getTheme(themeName) : themes.getDefaultTheme(); theme.setWebResourceLocator(getResourceLocator(request)); } return theme; } public NavigationTree getNavigationTree() throws ServletException { if (navigationTree == null || !isCacheComponents()) { String navTreeName = servletOptions.getNavigationTreeName(); Project project = getProject(); navigationTree = navTreeName != null ? project.getNavigationTree(navTreeName) : project.getDefaultNavigationTree(); } return navigationTree; } public HttpLoginManager getLoginManager() throws ServletException { if (servletOptions.getLoginManagerName() != null && (loginManager == null || !isCacheComponents())) loginManager = getProject().getLoginManagers().getLoginManager(servletOptions.getLoginManagerName()); else loginManager = getProject().getLoginManagers().getDefaultManager(); return loginManager; } public ProjectComponent getProjectComponent() { try { int compFlags = XdmComponentFactory.XDMCOMPFLAG_CACHE_ALWAYS; if (getRuntimeEnvironmentFlags() .flagIsSet(RuntimeEnvironmentFlags.DEVELOPMENT | RuntimeEnvironmentFlags.FRAMEWORK_DEVELOPMENT)) compFlags |= XdmComponentFactory.XDMCOMPFLAG_ALLOWRELOAD; final boolean forceReload = isForcedReloadOfProjectRequested(); // never store the ProjectComponent instance since it may change if it needs to be reloaded // (always use the factory get() method) ProjectComponent projectComponent = (ProjectComponent) XdmComponentFactory.get(projectComponentClass, projectSourceFileName, compFlags, forceReload); if (lastProjectComponentRetrievedId != projectComponent.hashCode()) { if (projectComponent.getErrors().size() > 0) { String message = "You have " + projectComponent.getErrors().size() + " error(s) in the project. To see the messages, visit\nhttp://<your-host>" + getServletContext().getServletContextName() + "/console/project/input-source#errors."; if (log.isErrorEnabled()) log.error(message); else System.err.println(message); for (int i = 0; i < projectComponent.getErrors().size(); i++) System.err.println(projectComponent.getErrors().get(i)); } if (projectComponent.getWarnings().size() > 0) { String message = "You have " + projectComponent.getWarnings().size() + " warning(s) in the project. To see the messages, visit\nhttp://<your-host>" + getServletContext().getServletContextName() + "/console/project/input-source#warnings."; if (log.isWarnEnabled()) log.warn(message); else System.out.println(message); } String[] listeners = servletOptions.getProjectLifecycleListenerClassNames(); if (listeners != null) { for (int i = 0; i < listeners.length; i++) { ProjectEvent event = new ProjectEvent(projectComponent.getProject()); Class listenerClass = Class.forName(listeners[i]); if (ProjectLifecyleListener.class.isAssignableFrom(listenerClass)) { ProjectLifecyleListener pll = (ProjectLifecyleListener) listenerClass.newInstance(); pll.projectLoadedFromXml(event); } else log.error("Unknown listener: " + listenerClass); } } // clear the currently cached project if there is one project = null; // save this for the next time so that we don't reinitialize or run the listeners again lastProjectComponentRetrievedId = projectComponent.hashCode(); } return projectComponent; } catch (Exception e) { throw new NestableRuntimeException(e); } } protected boolean isForcedReloadOfProjectRequested() { final Object request = REQUEST.get(); final boolean result; if (request != null) { // if the request parameter is given it means we want a reload final HttpServletRequest httpRequest = ((HttpServletRequest) request); if (httpRequest.getParameter(REQPARAMNAME_FORCE_PROJECT_RELOAD) != null) { // if we have already requested a reload during one of our project calls don't keep reloading if (httpRequest.getAttribute(REQPARAMNAME_FORCE_PROJECT_RELOAD) == null) { httpRequest.setAttribute(REQPARAMNAME_FORCE_PROJECT_RELOAD, Boolean.TRUE); return true; } } } return false; } public Project getProject() { if (project == null || !isCacheComponents()) { project = getProjectComponent().getProject(); clientServiceRequestHandlers.putAll(project.getClientServiceRequestHandlers()); String connProviderName = servletOptions.getDefaultConnectionProviderName(null); if (connProviderName != null) project.setDefaultConnectionProviderName(connProviderName); } return project; } public NavigationContext createNavigationContext(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { Project project = getProject(); // Setup the SQL Manager currently in use in case other parts of the application (like DAL) need it. // This is mainly used so that the SqlManager.getThreadDefaultSchema() will work properly for the case where // there is only one schema in the app (typical use). Basically this allows the generated DAL to pick up the // schema instance that is wrapping. SqlManager.setThreadSqlManager(project); Theme theme = getTheme(httpServletRequest); httpServletRequest.setAttribute(BasicDbHttpServletValueContext.REQATTRNAME_ACTIVE_THEME, theme); NavigationTree tree = null; if (isSecure()) { // check to see if there is an active user and the user wants a user-specific navigation tree AuthenticatedUser user = getLoginManager().getAuthenticatedUser(httpServletRequest); if (user instanceof NavigationControllerAuthenticatedUser) { NavigationControllerAuthenticatedUser ncUser = (NavigationControllerAuthenticatedUser) user; if (ncUser.hasUserSpecificNavigationTree()) tree = ncUser.getUserSpecificNavigationTree(this, httpServletRequest, httpServletResponse); } } // if we get to here it means the user is not overriding the current tree so we'll use the default if (tree == null) tree = getNavigationTree(); // if the tree is still null we've got a big problem if (tree == null) throw new ServletException("Navigation tree '" + servletOptions.getNavigationTreeName() + "' not found. Available: " + project.getNavigationTrees()); String activePageId = httpServletRequest.getPathInfo(); if (activePageId == null) activePageId = "/"; NavigationSkin skin = theme.getDefaultNavigationSkin(); return skin.createContext(this, httpServletRequest, httpServletResponse, tree, activePageId); } protected boolean logoutRequested(NavigationContext nc) throws ServletException, IOException { if (isSecure()) { String logoutActionReqParamValue = nc.getHttpRequest() .getParameter(servletOptions.getLogoutActionReqParamName()); if (logoutActionReqParamValue != null && TextUtils.getInstance().toBoolean(logoutActionReqParamValue)) { getLoginManager().logout(nc); return true; } } return false; } protected void renderPage(NavigationContext nc) throws ServletException, IOException { final HttpServletResponse httpResponse = nc.getHttpResponse(); if (isSecure()) { HttpLoginManager loginManager = getLoginManager(); LoginDialogMode loginDialogMode = LoginDialogMode.ACCESS_ALLOWED; if (loginManager != null) { loginDialogMode = loginManager.getLoginDialogMode(nc); // if we're getting input or we're denying login it means that the presentation is complete (HTML is already on the screen) if (loginDialogMode == LoginDialogMode.GET_INPUT || loginDialogMode == LoginDialogMode.LOGIN_DENIED) return; } // if we get to here, it means that the login dialog mode is either LOGIN_ACCEPTED (user has just logged in) or ACCESS_ALLOWED // which means that access was previously granted and the user is still valid // check to see if the user has recently logged in and is using the wrong navigation tree if (loginDialogMode == LoginDialogMode.LOGIN_ACCEPTED) { AuthenticatedUser user = nc.getAuthenticatedUser(); if (user instanceof NavigationControllerAuthenticatedUser) { NavigationControllerAuthenticatedUser ncUser = (NavigationControllerAuthenticatedUser) user; if (ncUser.hasUserSpecificNavigationTree()) { NavigationTree userTree = ncUser.getUserSpecificNavigationTree(this, nc.getHttpRequest(), httpResponse); if (userTree != null && nc.getOwnerTree() != userTree) { // we want to redirect back to the home page of the navigation tree so that the proper tree // will be picked up by the createContext() method ncUser.redirectToUserTree(nc); return; } } } } } NavigationPage activePage = nc.getActivePage(); if (activePage != null) { nc.getResponse().setContentType("text/html"); if (nc.isActivePageValid()) { // make any necessary state changes (such as permissions, conditionals, etc). activePage.makeStateChanges(nc); // check to see if we have static content and we're in development mode (because in development presumably we don't want // anything to be static since 'static' is a performance attribute, not functionality if (!nc.getRuntimeEnvironmentFlags().isDevelopment() && activePage.getFlags().flagIsSet(NavigationPage.Flags.STATIC_CONTENT)) { final HttpServletRequest httpRequest = nc.getHttpRequest(); String staticPageKey = httpRequest.getServletPath() + httpRequest.getPathInfo(); Date lastModfTime = (Date) staticPagesRendered.get(staticPageKey); // If the client sent an If-Modified-Since header equal or after the // servlet's last modified time, send a short "Not Modified" status code // Round down to the nearest second since client headers are in seconds if (lastModfTime != null && httpRequest.getMethod().equals("GET") && ((lastModfTime.getTime() / 1000 * 1000) <= httpRequest .getDateHeader("If-Modified-Since"))) { httpResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } else { Date now = new Date(); httpResponse.setDateHeader("Last-Modified", now.getTime()); staticPagesRendered.put(staticPageKey, now); } } // if we get to there we're not static content or we're static but being rendered for the first // time in this instance of the servlet if (activePage.isRawHandler(nc)) activePage.handlePageRaw(nc); else activePage.handlePage(nc.getResponse().getWriter(), nc); } else activePage.handleInvalidPage(nc.getResponse().getWriter(), nc); } else { Writer writer = nc.getResponse().getWriter(); NavigationSkin skin = nc.getSkin(); NavigationTree tree = nc.getOwnerTree(); skin.renderPageMetaData(writer, nc); skin.renderPageHeader(writer, nc); writer.write("No page located for path '" + nc.getActivePathFindResults().getSearchedForPath() + "'."); if (nc.getRuntimeEnvironmentFlags().flagIsSet(RuntimeEnvironmentFlags.DEVELOPMENT)) { writer.write("<pre>\n"); writer.write(tree.toString()); writer.write("</pre>\n"); } skin.renderPageFooter(writer, nc); } } protected void renderPageNotFound(NavigationContext nc) throws IOException, ServletException { final String queryString = nc.getHttpRequest().getQueryString(); final String rootUrl = nc.getRootUrl() + (queryString != null && queryString.length() > 0 ? ("?" + queryString) : ""); log.warn("Redirecting to the ROOT URL " + rootUrl + ": no active page located in NavigationTree '" + getNavigationTree().getName() + "' for '" + nc.getActivePathFindResults().getSearchedForPath() + "' -- did you set a default page in the tree? For example <page name=\"foo\" default=\"yes\"/>? This could also be a timeout event in the case of a user-based multiple navigation-tree application."); nc.getHttpResponse().sendRedirect(rootUrl); } protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { // record the starting time because it may be used by skins to show complete render times. httpServletRequest.setAttribute(REQATTRNAME_RENDER_START_TIME, new Long(System.currentTimeMillis())); // [SNS] Retrieve the session once because for some reason if this is not done, IE doesn't create sessions properly // (Mozilla seems to be immune). If the session is not retrieved early, then dialog states don't work too well // so things like query select scroll states fail too. :-( // TODO: try and figure out why! httpServletRequest.getSession(); String clientServiceRequestIdentifier = httpServletRequest.getHeader(CLIENT_SERVICE_REQUEST_HEADER_NAME); ClientServiceRequestHandler clientServiceRequestHandler = null; if (clientServiceRequestIdentifier != null) { clientServiceRequestHandler = (ClientServiceRequestHandler) clientServiceRequestHandlers .get(clientServiceRequestIdentifier); if (clientServiceRequestHandler != null) { if (!clientServiceRequestHandler.isNavigationContextRequiredForClientService()) { clientServiceRequestHandler.handleClientServiceRequest(null, httpServletRequest, httpServletResponse); return; } // if we get to here it means that a navigation context is required for the service so we will call the service below // after computing the navigation context } else { log.error("Found a client service request handler header '" + CLIENT_SERVICE_REQUEST_HEADER_NAME + "' with value '" + clientServiceRequestIdentifier + "' but no handler class was found."); return; } } setThreadServletContext(getServletContext()); setThreadServlet(this); setThreadRequest(httpServletRequest); NavigationContext nc = createNavigationContext(httpServletRequest, httpServletResponse); setThreadNavigationContext(nc); if (clientServiceRequestHandler != null) { clientServiceRequestHandler.handleClientServiceRequest(nc, httpServletRequest, httpServletResponse); return; } if (nc.getActivePage() == null) { renderPageNotFound(nc); return; } if (logoutRequested(nc)) { httpServletResponse.sendRedirect(nc.getServletRootUrl()); return; } if (nc.isRedirectRequired()) { String url = nc.getActivePage().getUrl(nc); if (url.indexOf('?') == -1) // see if we've appened any parameters (if not, we want to include all) url = HttpUtils.appendParams(httpServletRequest, url, "*"); httpServletResponse.sendRedirect(url); return; } else renderPage(nc); // if we get to here it means no exceptions were thrown during initialization and the first get/post -- this // means we're going to assume our initialization was properly done and increment the persistent init count if (!initCountWritten) persistInitCount(); setThreadNavigationContext(null); } protected void doPost(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException { doGet(httpServletRequest, httpServletResponse); } }