/******************************************************************************* * Copyright (c) 2005 Oracle Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Gerry Kessler - initial API and implementation *******************************************************************************/ package org.eclipse.jst.jsf.core.internal.project.facet; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.emf.common.util.EList; import org.eclipse.jst.j2ee.model.IModelProvider; import org.eclipse.jst.j2ee.webapplication.WebapplicationFactory; import org.eclipse.jst.javaee.core.JavaeeFactory; import org.eclipse.jst.javaee.web.WebAppVersionType; import org.eclipse.jst.javaee.web.WebFactory; import org.eclipse.jst.jsf.core.JSFVersion; import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; import org.eclipse.jst.jsf.core.internal.Messages; import org.eclipse.jst.jsf.core.internal.jsflibraryregistry.ArchiveFile; import org.eclipse.jst.jsf.core.internal.jsflibraryregistry.JSFLibrary; import org.eclipse.osgi.util.NLS; import org.eclipse.wst.common.frameworks.datamodel.IDataModel; /** * */ @SuppressWarnings("deprecation") public abstract class JSFUtils { /** * The default name for the Faces servlet */ public static final String JSF_DEFAULT_SERVLET_NAME = "Faces Servlet"; //$NON-NLS-1$ /** * The default name of the Faces servlet class */ public static final String JSF_SERVLET_CLASS = "javax.faces.webapp.FacesServlet"; //$NON-NLS-1$ /** * The name of the context parameter used for JSF configuration files */ public static final String JSF_CONFIG_CONTEXT_PARAM = "javax.faces.CONFIG_FILES"; //$NON-NLS-1$ /** * The name of the context parameter used for defining the default JSP file extension */ public static final String JSF_DEFAULT_SUFFIX_CONTEXT_PARAM = "javax.faces.DEFAULT_SUFFIX"; //$NON-NLS-1$ /** * The path to the default application configuration file */ public static final String JSF_DEFAULT_CONFIG_PATH = "/WEB-INF/faces-config.xml"; //$NON-NLS-1$ /** * Default URL mapping to faces servlet */ public static final String JSF_DEFAULT_URL_MAPPING = "/faces/*"; //$NON-NLS-1$ /** * the key for implementation libraries in persistent properties */ public static final String PP_JSF_IMPLEMENTATION_LIBRARIES = "jsf.implementation.libraries"; //$NON-NLS-1$ /** * the key for component libraries in persistent properties */ public static final String PP_JSF_COMPONENT_LIBRARIES = "jsf.component.libraries"; //$NON-NLS-1$ /** * the key for implementation type in persistent properties */ public static final String PP_JSF_IMPLEMENTATION_TYPE = "jsf.implementation.type"; //$NON-NLS-1$ private static final String DEFAULT_DEFAULT_MAPPING_SUFFIX = "jsp"; //$NON-NLS-1$ /** * In cases where there is no servlet mapping defined (where it can be implicit and provided * by the container), this system property can be used to provide the "implicit" servlet * mapping. */ private static final String SYS_PROP_SERVLET_MAPPING = "org.eclipse.jst.jsf.servletMapping"; //$NON-NLS-1$ private final JSFVersion _version; private final IModelProvider _modelProvider; /** * @param version * @param modelProvider */ protected JSFUtils(final JSFVersion version, final IModelProvider modelProvider) { _version = version; _modelProvider = modelProvider; } /** * @return the jsf version that this instance is for. */ public final JSFVersion getVersion() { return _version; } /** * @param config * @return servlet display name to use from wizard data model */ protected final String getDisplayName(IDataModel config) { String displayName = config.getStringProperty(IJSFFacetInstallDataModelProperties.SERVLET_NAME); if (displayName == null || displayName.trim().length() == 0) displayName = JSF_DEFAULT_SERVLET_NAME; return displayName.trim(); } /** * @param config * @return servlet display name to use from wizard data model */ protected final String getServletClassname(IDataModel config) { String className = config.getStringProperty(IJSFFacetInstallDataModelProperties.SERVLET_CLASSNAME); if (className == null || className.trim().equals("")) //$NON-NLS-1$ className = JSF_SERVLET_CLASS; return className.trim(); } /** * @return IModelProvider */ public final IModelProvider getModelProvider() { Object webAppObj = _modelProvider.getModelObject(); if (webAppObj == null) { return null; } return _modelProvider; } /** * @param configPath */ public final void createConfigFile(IPath configPath) { FileOutputStream os = null; // final String QUOTE = new String(new char[] { '"' }); try { IPath dirPath = configPath.removeLastSegments(1); dirPath.toFile().mkdirs(); File file = configPath.toFile(); file.createNewFile(); os = new FileOutputStream(file); printConfigFile(os); } catch (FileNotFoundException e) { JSFCorePlugin.log(IStatus.ERROR, Messages.JSFUtils_ErrorCreatingConfigFile, e); } catch (IOException e) { JSFCorePlugin.log(IStatus.ERROR, Messages.JSFUtils_ErrorCreatingConfigFile, e); } finally { if (os != null) { try { os.close(); } catch (IOException e) { JSFCorePlugin.log(IStatus.ERROR, Messages.JSFUtils_ErrorClosingConfigFile, e); } } } } /** * @param out */ protected final void printConfigFile(final OutputStream out) { PrintWriter pw = null; try { pw = new PrintWriter(out); doVersionSpecificConfigFile(pw); } finally { if (pw != null) { pw.close(); } } } /** * @param pw */ public abstract void doVersionSpecificConfigFile(final PrintWriter pw); /** * @param webAppObj * @return true if this going into a JavaEE (1.5 and later) or a J2EE (1.4 and earlier) * configured application. */ public boolean isJavaEE(final Object webAppObj) { if (webAppObj instanceof org.eclipse.jst.javaee.web.WebApp) { org.eclipse.jst.javaee.web.WebApp webApp = (org.eclipse.jst.javaee.web.WebApp)webAppObj; return WebAppVersionType.VALUES.contains(webApp.getVersion()); } return false; } /** * @param config * @return list of URL patterns from the datamodel */ protected final List<String> getServletMappings(final IDataModel config) { final List<String> mappings = new ArrayList<String>(); final String[] patterns = (String[])config.getProperty(IJSFFacetInstallDataModelProperties.SERVLET_URL_PATTERNS); if (patterns != null) { for (final String pattern : patterns) { mappings.add(pattern); } } return mappings; } /** * Does an update of the web application's config file. * * @param webApp must be WebApp of the appropriate type. * @param config * @throws ClassCastException if webApp is not the appropriate type. */ public abstract void updateWebApp(Object webApp, IDataModel config); /** * Called on a facet uninstall to remove JSF related changes. * @param webApp */ public abstract void rollbackWebApp(Object webApp); /** * @param fileExtension * @return true if the file extension is deemed to be for a JSF. */ protected boolean isValidKnownExtension(String fileExtension) { //Bug 313830 - JSFUtils should use content type instead file extension in isValidKnownExtension. List<String> validKnownExtensions = new ArrayList<String>(); for (String contentTypeId: new String[]{ "org.eclipse.jst.jsp.core.jspsource", //$NON-NLS-1$ "org.eclipse.jst.jsp.core.jspfragmentsource", //$NON-NLS-1$ "jsf.facelet"}) { //$NON-NLS-1$ IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeId); if (contentType != null) { String[] contentTypeExts = contentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC); if (contentTypeExts != null) { validKnownExtensions.addAll(Arrays.asList(contentTypeExts)); } } } if (fileExtension != null) { return validKnownExtensions.contains(fileExtension); } return false; } /** * @param resource * @return true if the resource is deemed to be a JSF page. */ protected boolean isJSFPage(IResource resource) { // currently always return true if resource != null. // need to find quick way of determining whether this is a JSF JSP Page boolean isJSFPage = true; if (resource == null) { isJSFPage = false; } return isJSFPage; } /** * @param webApp * @return the default file extension from the context param. Default is * "jsp" if no context param. */ protected String getDefaultSuffix(Object webApp) { String contextParam = null; if (webApp != null) { if(isJavaEE(webApp)) { contextParam = JEEUtils.getContextParam((org.eclipse.jst.javaee.web.WebApp) webApp, JSF_DEFAULT_SUFFIX_CONTEXT_PARAM); } else { contextParam = J2EEUtils.getContextParam((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, JSF_DEFAULT_SUFFIX_CONTEXT_PARAM); } } if (contextParam == null) { return getDefaultDefaultSuffix(); } return normalizeSuffix(contextParam); } /** * @return the default value for the default mapping suffix */ protected String getDefaultDefaultSuffix() { return DEFAULT_DEFAULT_MAPPING_SUFFIX; } /** * @param name * @param value * @return the */ protected final String calculateSuffix(final String name, final String value) { if (name != null && JSF_DEFAULT_SUFFIX_CONTEXT_PARAM.equals(name .trim())) { return normalizeSuffix(value != null ? value.trim() : null); } return null; } /** * @param defSuffix * @return the suffix value with any leading dot removed */ protected final String normalizeSuffix(String defSuffix) { if (defSuffix != null && defSuffix.startsWith(".")) //$NON-NLS-1$ { defSuffix = defSuffix.substring(1); } return defSuffix; } /** * Holds all the obsolete JSF Library stuff. This will go away post-Helios. * @author cbateman * */ public static class JSFLibraryHandler { /** * Construct an array that hold paths for all JARs in a JSF library. * * @param jsfLib * @param logMissingJar true to log an error for each invalid JAR. * @return elements */ public final IPath[] getJARPathforJSFLib(JSFLibrary jsfLib, boolean logMissingJar) { EList archiveFiles = jsfLib.getArchiveFiles(); int numJars = archiveFiles.size(); IPath[] elements = new IPath[numJars]; ArchiveFile ar = null; for (int i= 0; i < numJars; i++) { ar = (ArchiveFile)archiveFiles.get(i); if ( !ar.exists() && logMissingJar ) { logErroronMissingJAR(jsfLib, ar); } elements[i] = new Path(((ArchiveFile)archiveFiles.get(i)).getResolvedSourceLocation()).makeAbsolute(); } return elements; } private int numberofValidJar(EList archiveFiles) { int total = 0; final Iterator it = archiveFiles.iterator(); ArchiveFile ar = null; while(it.hasNext()) { ar = (ArchiveFile) it.next(); if (ar.exists()) { total++; } } return total; } private void logErroronMissingJAR(JSFLibrary jsfLib, ArchiveFile ar) { String msg = NLS.bind(Messages.JSFUtils_MissingJAR, ar.getName(), jsfLib.getLabel()); JSFCorePlugin.log(IStatus.ERROR, msg); } /** * Construct an array that hold paths for all JARs in a JSF library. * However, archive files that no longer exist are filtered out. * * @param jsfLib * @param logMissingJar true to log an error for each invalid JAR. * @return elements */ public final IPath[] getJARPathforJSFLibwFilterMissingJars(JSFLibrary jsfLib, boolean logMissingJar) { EList archiveFiles = jsfLib.getArchiveFiles(); int numJars = numberofValidJar(archiveFiles); IPath[] elements = new IPath[numJars]; ArchiveFile ar = null; int idxValidJar = 0; for (int i= 0; i < archiveFiles.size(); i++) { ar = (ArchiveFile)archiveFiles.get(i); if ( !ar.exists() ) { if (logMissingJar) { logErroronMissingJAR(jsfLib, ar); } } else { elements[idxValidJar] = new Path(((ArchiveFile)archiveFiles.get(i)).getResolvedSourceLocation()).makeAbsolute(); idxValidJar++; } } return elements; } } /** * Finds and returns a JSF Servlet definition, or null if servlet is not defined. * * @param webApp * @return Servlet or null */ protected Object findJSFServlet(Object webApp) { if(isJavaEE(webApp)) { return JEEUtils.findServlet((org.eclipse.jst.javaee.web.WebApp) webApp, JSF_SERVLET_CLASS); } return J2EEUtils.findServlet((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, JSF_SERVLET_CLASS); } /** * Creates servlet reference in WebApp if not present or updates servlet * name if found using the passed configuration. * * @param webApp * @param config * @param servlet * @return Servlet servlet - if passed servlet was null, will return created * servlet */ protected Object createOrUpdateServletRef(final Object webApp, final IDataModel config, Object servlet) { String displayName = getDisplayName(config); String className = getServletClassname(config); if(isJavaEE(webApp)) { return JEEUtils.createOrUpdateServletRef((org.eclipse.jst.javaee.web.WebApp) webApp, displayName, className, (org.eclipse.jst.javaee.web.Servlet) servlet); } return J2EEUtils.createOrUpdateServletRef((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, displayName, className, (org.eclipse.jst.j2ee.webapplication.Servlet) servlet); } /** * Creates servlet-mappings for the servlet for 2.5 WebModules or greated * * @param webApp * @param urlMappingList * - list of string values to be used in url-pattern for * servlet-mapping * @param servlet */ protected void setUpURLMappings(final Object webApp, final List<String> urlMappingList, final Object servlet) { if(isJavaEE(webApp)) { JEEUtils.setUpURLMappings((org.eclipse.jst.javaee.web.WebApp) webApp, urlMappingList, (org.eclipse.jst.javaee.web.Servlet) servlet); } else { J2EEUtils.setUpURLMappings((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, urlMappingList, (org.eclipse.jst.j2ee.webapplication.Servlet) servlet); } } /** * Removes servlet-mappings for servlet using servlet-name for >= 2.5 WebModules. * @param webApp * @param servlet */ protected void removeURLMappings(final Object webApp, final Object servlet) { if(isJavaEE(webApp)) { JEEUtils.removeURLMappings((org.eclipse.jst.javaee.web.WebApp) webApp, (org.eclipse.jst.javaee.web.Servlet) servlet); } else { J2EEUtils.removeURLMappings((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, (org.eclipse.jst.j2ee.webapplication.Servlet) servlet); } } /** * Removes servlet definition * @param webApp * @param servlet */ protected void removeJSFServlet(final Object webApp, final Object servlet) { if(isJavaEE(webApp)) { JEEUtils.removeServlet((org.eclipse.jst.javaee.web.WebApp) webApp, (org.eclipse.jst.javaee.web.Servlet) servlet); } else { J2EEUtils.removeServlet((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, (org.eclipse.jst.j2ee.webapplication.Servlet) servlet); } } /** * Removes context-param * @param webApp */ protected void removeJSFContextParams(final Object webApp) { if(isJavaEE(webApp)) { JEEUtils.removeContextParam((org.eclipse.jst.javaee.web.WebApp) webApp, JSF_CONFIG_CONTEXT_PARAM); } else { J2EEUtils.removeContextParam((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, JSF_CONFIG_CONTEXT_PARAM); } } /** * Creates or updates context-params * @param webApp * @param config */ protected void setupContextParams(final Object webApp, final IDataModel config) { final String paramValue = config.getStringProperty(IJSFFacetInstallDataModelProperties.CONFIG_PATH); if (paramValue != null && !paramValue.equals(JSF_DEFAULT_CONFIG_PATH)) { if(isJavaEE(webApp)) { JEEUtils.setupContextParam((org.eclipse.jst.javaee.web.WebApp) webApp, JSF_CONFIG_CONTEXT_PARAM, paramValue); } else { J2EEUtils.setupContextParam((org.eclipse.jst.j2ee.webapplication.WebApp) webApp, JSF_CONFIG_CONTEXT_PARAM, paramValue); } } } /** * @param map * @param webApp * @return extension from map. Will return null if file extension not found * in url patterns. */ protected String getFileExtensionFromMap(final Object webApp, final Object map) { if(isJavaEE(webApp)) { return JEEUtils.getFileExtensionFromMap((org.eclipse.jst.javaee.web.ServletMapping) map); } return J2EEUtils.getFileExtensionFromMap((org.eclipse.jst.j2ee.webapplication.ServletMapping) map); } /** * @param webApp * @param map * @return prefix mapping. may return null. */ protected String getPrefixMapping(final Object webApp, final Object map) { if(isJavaEE(webApp)) { return JEEUtils.getPrefixMapping((org.eclipse.jst.javaee.web.ServletMapping) map); } return J2EEUtils.getPrefixMapping((org.eclipse.jst.j2ee.webapplication.ServletMapping) map); } /** * @param webAppObj * @param resource * @param existingURL * @return the modified url path for the (possibly) jsf resource. */ public IPath getFileUrlPath(Object webAppObj, IResource resource, IPath existingURL) { // if not a JSF page, do nothing if (!isJSFPage(resource)) { return null; } Object servlet = findJSFServlet(webAppObj); List implicitServletMappings = new ArrayList(); if (servlet == null) { servlet = createImplicitServletAndMapping(webAppObj, implicitServletMappings); } String defaultSuffix = getDefaultSuffix(webAppObj); // is the resource using default_suffix String fileExtension = resource.getFileExtension(); boolean canUseExtensionMapping = fileExtension != null && fileExtension.equalsIgnoreCase(defaultSuffix); // if not using default extension and is not a known file extension, // then we will abort if (!canUseExtensionMapping && !isValidKnownExtension(resource.getFileExtension())) return null; if(isJavaEE(webAppObj)) { org.eclipse.jst.javaee.web.WebApp webApp = (org.eclipse.jst.javaee.web.WebApp) webAppObj; final String servletName = ((org.eclipse.jst.javaee.web.Servlet) servlet).getServletName(); String foundFileExtension = null; List mappings; if (webApp.getServletMappings().size() > 0) { mappings = webApp.getServletMappings(); } else { mappings = implicitServletMappings; } for (final org.eclipse.jst.javaee.web.ServletMapping map : (List<org.eclipse.jst.javaee.web.ServletMapping>) mappings) { if (map != null && map.getServletName() != null && map.getServletName().trim().equals(servletName.trim())) { foundFileExtension = getFileExtensionFromMap(webAppObj, map); if (foundFileExtension != null && canUseExtensionMapping) { return existingURL.removeFileExtension() .addFileExtension(foundFileExtension); } String foundPrefixMapping = getPrefixMapping(webAppObj, map); if (foundPrefixMapping != null) { return new Path(foundPrefixMapping).append(existingURL); } } } if (!canUseExtensionMapping && foundFileExtension != null) { // we could prompt user that this may not work... // for now we will return the extension mapping return existingURL.removeFileExtension().addFileExtension( foundFileExtension); } // we could, at this point, add a url mapping to the faces servlet, // or prompt user that it may be a good idea to add one... } else { List mappings; if (((org.eclipse.jst.j2ee.webapplication.Servlet)servlet).getMappings().size() > 0) { mappings = ((org.eclipse.jst.j2ee.webapplication.Servlet)servlet).getMappings(); } else { mappings = implicitServletMappings; } Iterator itMappings = mappings.iterator(); org.eclipse.jst.j2ee.webapplication.ServletMapping map = null; String foundFileExtension = null; String foundPrefixMapping = null; while (itMappings.hasNext()) { map = (org.eclipse.jst.j2ee.webapplication.ServletMapping)itMappings.next(); foundFileExtension = getFileExtensionFromMap(webAppObj, map); if (foundFileExtension != null && canUseExtensionMapping) { return existingURL.removeFileExtension().addFileExtension(foundFileExtension); } if (foundPrefixMapping == null) { foundPrefixMapping = getPrefixMapping(webAppObj, map); } } if (foundPrefixMapping != null) { return new Path(foundPrefixMapping).append(existingURL); } if (! canUseExtensionMapping && foundFileExtension != null){ //we could prompt user that this may not work... //for now we will return the extension mapping return existingURL.removeFileExtension().addFileExtension(foundFileExtension); } // we could, at this point, add a url mapping to the faces servlet, // or prompt user that it may be a good idea to add one... } return null; } /** * This creates a servlet and servlet mapping suitable for * {@link #getFileUrlPath(Object, IResource, IPath)} to be able to proceed, but the returned * objects are not fully-initialized for any and all purposes. USE WITH CAUTION. * * @param webApp Web App object. * @param mappings List to which the that the created servlet mapping will be added. * @return Servlet instance (and a servlet mapping added to mappings). */ private Object createImplicitServletAndMapping(Object webApp, List mappings) { Object servlet; String sysPropServletMapping = System.getProperty(SYS_PROP_SERVLET_MAPPING); if (sysPropServletMapping == null) { sysPropServletMapping = JSF_DEFAULT_URL_MAPPING; } if (isJavaEE(webApp)) { servlet = WebFactory.eINSTANCE.createServlet(); ((org.eclipse.jst.javaee.web.Servlet)servlet).setServletName(JSF_DEFAULT_SERVLET_NAME); org.eclipse.jst.javaee.web.ServletMapping mapping = WebFactory.eINSTANCE.createServletMapping(); mapping.setServletName(JSF_DEFAULT_SERVLET_NAME); org.eclipse.jst.javaee.core.UrlPatternType pattern = JavaeeFactory.eINSTANCE.createUrlPatternType(); pattern.setValue(sysPropServletMapping); mapping.getUrlPatterns().add(pattern); mappings.add(mapping); } else { servlet = WebapplicationFactory.eINSTANCE.createServlet(); ((org.eclipse.jst.j2ee.webapplication.Servlet)servlet).setServletName(JSF_DEFAULT_SERVLET_NAME); org.eclipse.jst.j2ee.webapplication.ServletMapping mapping = WebapplicationFactory.eINSTANCE.createServletMapping(); mapping.setUrlPattern(sysPropServletMapping); mappings.add(mapping); } return servlet; } }