/******************************************************************************* * 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: * Ian Trimble - initial API and implementation *******************************************************************************/ package org.eclipse.jst.jsf.core.jsfappconfig; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.core.runtime.content.IContentTypeManager; import org.eclipse.emf.common.util.EList; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jst.j2ee.common.ParamValue; import org.eclipse.jst.j2ee.internal.J2EEVersionConstants; import org.eclipse.jst.j2ee.model.IModelProvider; import org.eclipse.jst.j2ee.model.ModelProviderManager; import org.eclipse.jst.j2ee.web.componentcore.util.WebArtifactEdit; import org.eclipse.jst.j2ee.webapplication.ContextParam; import org.eclipse.jst.j2ee.webapplication.WebApp; import org.eclipse.jst.jsf.common.internal.componentcore.AbstractVirtualComponentQuery.DefaultVirtualComponentQuery; import org.eclipse.jst.jsf.core.IJSFCoreConstants; import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; import org.eclipse.jst.jsf.core.internal.Messages; import org.eclipse.jst.jsf.facesconfig.emf.FacesConfigFactory; import org.eclipse.jst.jsf.facesconfig.emf.ManagedBeanExtensionType; import org.eclipse.jst.jsf.facesconfig.emf.ManagedBeanType; import org.eclipse.osgi.util.NLS; import org.eclipse.wst.common.componentcore.resources.IVirtualFolder; import org.eclipse.wst.common.project.facet.core.IFacetedProject; import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion; import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager; /** * JSFAppConfigUtils provides utility methods useful in processing of a JSF * application configuration. * * <p><b>Provisional API - subject to change</b></p> * * @author Ian Trimble - Oracle */ public class JSFAppConfigUtils { /** * Name of JSF CONFIG_FILES context parameter ("javax.faces.CONFIG_FILES"). */ public static final String CONFIG_FILES_CONTEXT_PARAM_NAME = "javax.faces.CONFIG_FILES"; //$NON-NLS-1$ /** * Location in JAR file of application configuration resource file * ("META-INF/faces-config.xml"). */ public static final String FACES_CONFIG_IN_JAR_PATH = "META-INF/faces-config.xml"; //$NON-NLS-1$ /** ID of managed bean's description instance to indicate content identifies bean's "source" **/ public static final String MANAGEDBEAN_SOURCE_ID = "MANAGEDBEAN_SOURCE_ID"; //$NON-NLS-1$ /** Indicates that a managed bean was defined in a faces-config file **/ public static final String MANAGEDBEAN_SOURCE_FACES_CONFIG_FILE = "FACES_CONFIG_FILE"; //$NON-NLS-1$ /** Indicates that a managed bean was defined by a JSF annotation **/ public static final String MANAGEDBEAN_SOURCE_JSF_ANNOTATION = "JSF_ANNOTATION"; //$NON-NLS-1$ /** Indicates that a managed bean was defined by a CDI annotation **/ public static final String MANAGEDBEAN_SOURCE_CDI_ANNOTATION = "CDI_ANNOTATION"; //$NON-NLS-1$ /** * @param project * @param minVersion * @return true if project is a JSF facet project and the version of the project * is at least minVersion. */ public static boolean isValidJSFProject(IProject project, String minVersion) { boolean isValid = false; final IProjectFacetVersion projectFacetVersion = getProjectFacet(project); if (projectFacetVersion != null) { try { final String versionString = projectFacetVersion.getVersionString(); final Comparator comparator = projectFacetVersion.getProjectFacet().getVersionComparator(); final int compareToMin = comparator.compare(versionString, minVersion); if (compareToMin >=0) { return true; } } catch (CoreException ce) { JSFCorePlugin.log(ce, "Error checking facet version"); //$NON-NLS-1$ } } return isValid; } /** * Tests if the passed IProject instance is a valid JSF project in the * following ways: * <ul> * <li>project is not null and is accessible, </li> * <li>project has the JSF facet set on it.</li> * </ul> * * @param project IProject instance to be tested. * @return true if the IProject instance is a valid JSF project, else * false. */ public static boolean isValidJSFProject(IProject project) { boolean isValid = false; IProjectFacetVersion projectFacet = getProjectFacet(project); if (projectFacet != null) { isValid = true; } return isValid; } /** * Get the facet version for the project * @param project * @return the project facet version or null if could not be found or if * project is not accessible */ public static IProjectFacetVersion getProjectFacet(IProject project) { //check for null or inaccessible project if (project != null && project.isAccessible()) { //check for JSF facet on project try { IFacetedProject facetedProject = ProjectFacetsManager.create(project); if (facetedProject != null) { Set projectFacets = facetedProject.getProjectFacets(); Iterator itProjectFacets = projectFacets.iterator(); while (itProjectFacets.hasNext()) { IProjectFacetVersion projectFacetVersion = (IProjectFacetVersion)itProjectFacets.next(); if (IJSFCoreConstants.JSF_CORE_FACET_ID.equals(projectFacetVersion.getProjectFacet().getId())) { return projectFacetVersion; } } } } catch(CoreException ce) { //log error JSFCorePlugin.log(IStatus.ERROR, ce.getLocalizedMessage(), ce); } } return null; } /** * Gets an IVirtualFolder instance which represents the root context's * web content folder. * * @param project IProject instance for which to get the folder. * @return IVirtualFolder instance which represents the root context's * web content folder. * @deprecated Call DefaultVirtualComponentQuery.getWebContentFolder instead */ public static IVirtualFolder getWebContentFolder(IProject project) { return new DefaultVirtualComponentQuery().getWebContentFolder(project); } /** * Gets an IPath instance representing the path of the passed IFile * instance relative to the web content folder. * * @param file IFile instance for which a path is required. * @return IPath instance representing the path relative to the web content * folder. */ public static IPath getWebContentFolderRelativePath(IFile file) { IPath path = null; if (file != null) { IVirtualFolder webContentFolder = getWebContentFolder(file.getProject()); if (webContentFolder != null) { IPath webContentPath = webContentFolder.getProjectRelativePath(); IPath filePath = file.getProjectRelativePath(); int matchingFirstSegments = webContentPath.matchingFirstSegments(filePath); path = filePath.removeFirstSegments(matchingFirstSegments); } } return path; } /** * Gets list of application configuration file names as listed in the JSF * CONFIG_FILES context parameter ("javax.faces.CONFIG_FILES"). Will return * an empty list if WebArtifactEdit is null, if WebApp is null, if context * parameter does not exist, or if trimmed context parameter's value is * an empty String. * * @param project IProject instance for which to get the context * parameter's value. * @return List of application configuration file names as listed in the * JSF CONFIG_FILES context parameter ("javax.faces.CONFIG_FILES"); list * may be empty. */ public static List getConfigFilesFromContextParam(IProject project) { List filesList = Collections.EMPTY_LIST; if (isValidJSFProject(project)) { IModelProvider provider = ModelProviderManager.getModelProvider(project); Object webAppObj = provider.getModelObject(); if (webAppObj != null){ if (webAppObj instanceof WebApp) filesList = getConfigFilesForJ2EEApp(project); else if (webAppObj instanceof org.eclipse.jst.javaee.web.WebApp) filesList = getConfigFilesForJEEApp((org.eclipse.jst.javaee.web.WebApp)webAppObj); } } return filesList; } private static List getConfigFilesForJEEApp(org.eclipse.jst.javaee.web.WebApp webApp) { String filesString = null; List contextParams = webApp.getContextParams(); Iterator itContextParams = contextParams.iterator(); while (itContextParams.hasNext()) { org.eclipse.jst.javaee.core.ParamValue paramValue = (org.eclipse.jst.javaee.core.ParamValue)itContextParams.next(); if (paramValue.getParamName().equals(CONFIG_FILES_CONTEXT_PARAM_NAME)) { filesString = paramValue.getParamValue(); break; } } return parseFilesString(filesString); } private static List getConfigFilesForJ2EEApp(IProject project){ List filesList = new ArrayList(); WebArtifactEdit webArtifactEdit = WebArtifactEdit.getWebArtifactEditForRead(project); if (webArtifactEdit != null) { try { WebApp webApp = null; try { webApp = webArtifactEdit.getWebApp(); } catch(ClassCastException cce) { //occasionally thrown from WTP code in RC3 and possibly later JSFCorePlugin.log(IStatus.ERROR, cce.getLocalizedMessage(), cce); return filesList; } if (webApp != null) { String filesString = null; //need to branch here due to model version differences (BugZilla #119442) if (webApp.getVersionID() == J2EEVersionConstants.WEB_2_3_ID) { EList contexts = webApp.getContexts(); Iterator itContexts = contexts.iterator(); while (itContexts.hasNext()) { ContextParam contextParam = (ContextParam)itContexts.next(); if (contextParam.getParamName().equals(CONFIG_FILES_CONTEXT_PARAM_NAME)) { filesString = contextParam.getParamValue(); break; } } } else { EList contextParams = webApp.getContextParams(); Iterator itContextParams = contextParams.iterator(); while (itContextParams.hasNext()) { ParamValue paramValue = (ParamValue)itContextParams.next(); if (paramValue.getName().equals(CONFIG_FILES_CONTEXT_PARAM_NAME)) { filesString = paramValue.getValue(); break; } } } filesList = parseFilesString(filesString); } } finally { webArtifactEdit.dispose(); } } return filesList; } private static List parseFilesString(String filesString) { List filesList = new ArrayList(); if (filesString != null && filesString.trim().length() > 0) { StringTokenizer stFilesString = new StringTokenizer(filesString, ","); //$NON-NLS-1$ while (stFilesString.hasMoreTokens()) { String configFile = stFilesString.nextToken().trim(); filesList.add(configFile); } } return filesList; } /** * Gets list of JAR file names, where each file name represents a JAR on * the classpath that contains a /META-INF/faces-config.xml entry. Will * return an empty list if no such JAR files are located. * * @param project IProject instance for which to scan the classpath. * @return List of JAR file names, where each file name represents a JAR * on the classpath that contains a ...META-INF/faces-config.xml entry; * list may be empty. * @throws CoreException Thrown when underlying calls into JavaCore fail. * @throws IOException Thrown when attempt to open JAR to determine if it * contains a /META-INF/faces-config.xml entry fails. */ public static List getConfigFileJARsFromClasspath(IProject project) throws CoreException, IOException { ArrayList JARsList = new ArrayList(); if (project.isAccessible() && project.hasNature(JavaCore.NATURE_ID)) { IJavaProject javaProject = JavaCore.create(project); if (javaProject != null) { IClasspathEntry[] classpathEntries = javaProject.getResolvedClasspath(true); if (classpathEntries != null && classpathEntries.length > 0) { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); for (int i = 0; i < classpathEntries.length; i++) { IClasspathEntry classpathEntry = classpathEntries[i]; if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { IPath libraryPath = classpathEntry.getPath(); if (libraryPath.getFileExtension() != null && libraryPath.getFileExtension().length() > 0) { if (libraryPath.getDevice() == null && workspaceRoot.getProject(libraryPath.segment(0)).exists()) { libraryPath = workspaceRoot.getFile(libraryPath).getLocation(); } String libraryPathString = libraryPath.toString(); JarFile jarFile = null; try { //check existence first [222249] File file = new File(libraryPathString); if (file.exists()) { jarFile = new JarFile(file, false); if (jarFile != null) { JarEntry jarEntry = jarFile.getJarEntry(FACES_CONFIG_IN_JAR_PATH); if (jarEntry != null) { JARsList.add(libraryPathString); } } } } catch (FileNotFoundException fnfex) { //should not get here, but eat error since this could only occur in under strange circumstances [222249] } catch(IOException ioe) { JSFCorePlugin.log( IStatus.ERROR, NLS.bind(Messages.JSFAppConfigUtils_ErrorOpeningJarFile, libraryPathString), ioe); } finally { if (jarFile != null) { jarFile.close(); } } } } } } } } return JARsList; } //Bug 306451 - [JSF2.0] Support for Implicit navigation /** * Get all files that are possible destinations for implicit navigation (JSF 2.x or greater). * * @param fromFile IFile instance that navigation is from. * @return List of IFile instances that are possible destinations. */ public static List<IFile> getImplicitNavigationFiles(final IFile fromFile) { List<IFile> files = new ArrayList<IFile>(); if (fromFile != null) { final IProject project = fromFile.getProject(); if (isValidJSFProject(project, IJSFCoreConstants.JSF_VERSION_2_0)) { final IVirtualFolder webContentFolder = new DefaultVirtualComponentQuery().getWebContentFolder(project); if (webContentFolder != null) { final IContainer[] rootContainers = webContentFolder.getUnderlyingFolders(); if (rootContainers != null && rootContainers.length > 0) { for (int i = 0; i < rootContainers.length; i++) { try { rootContainers[i].accept(new JSFAppConfigUtils().new ImplicitNavigationResourceProxyVisitor(files), IResource.NONE); } catch(CoreException cEx) { JSFCorePlugin.log(IStatus.ERROR, cEx.getLocalizedMessage(), cEx); } } files.remove(fromFile); } } } } return files; } /** * Sets the "source" (one of the MANAGEDBEAN_SOURCE_* constants) of the managed bean. * @param managedBean Managed bean instance. * @param source "Source" of the managed bean. */ public static void setManagedBeanSource(final ManagedBeanType managedBean, final String source) { if (managedBean != null) { ManagedBeanExtensionType extension = getManagedBeanSourceExtension(managedBean); if (extension == null) { extension = FacesConfigFactory.eINSTANCE.createManagedBeanExtensionType(); extension.setId(MANAGEDBEAN_SOURCE_ID); managedBean.getManagedBeanExtension().add(extension); } final String content = extension.getTextContent(); if (content != null) { if (!content.trim().equals(source)) { extension.setTextContent(source); } } else { extension.setTextContent(source); } } } /** * Gets the "source" (one of the MANAGEDBEAN_SOURCE_* constants) of the managed bean. * @param managedBean Managed bean instance. * @return "Source" of the managed bean (assumes MANAGEDBEAN_SOURCE_FACES_CONFIG_FILE if not * set on the managed bean). */ public static String getManagedBeanSource(final ManagedBeanType managedBean) { String source = MANAGEDBEAN_SOURCE_FACES_CONFIG_FILE; final ManagedBeanExtensionType extension = getManagedBeanSourceExtension(managedBean); if (extension != null) { source = extension.getTextContent().trim(); } return source; } /** * Gets the managed bean instance's extension object that has ID of MANAGEDBEAN_SOURCE_ID. * @param managedBean Managed bean instance. * @return The managed bean's extension object that has ID of MANAGEDBEAN_SOURCE_ID. */ public static ManagedBeanExtensionType getManagedBeanSourceExtension(final ManagedBeanType managedBean) { ManagedBeanExtensionType extension = null; if (managedBean != null) { final EList extensions = managedBean.getManagedBeanExtension(); for (final Object curExtObj: extensions) { if (curExtObj instanceof ManagedBeanExtensionType) { final ManagedBeanExtensionType curExt = (ManagedBeanExtensionType) curExtObj; final String id = curExt.getId(); if (id != null && id.equals(MANAGEDBEAN_SOURCE_ID)) { extension = curExt; break; } } } } return extension; } /** * Returns <code>true</code> if the managed bean is defined in a faces-config file. * @param managedBean Managed bean instance to test. * @return <code>true</code> if the managed bean is defined in a faces-config file, else * <code>false</code>. */ public static boolean isDefinedInFacesConfigFile(final ManagedBeanType managedBean) { boolean ret = false; if (managedBean != null) { ret = !isDefinedByJSFAnnotation(managedBean) && !isDefinedByCDIAnnotation(managedBean); } return ret; } /** * Returns <code>true</code> if the managed bean is defined by a JSF annotation. * @param managedBean Managed bean instance to test. * @return <code>true</code> if the managed bean is defined by a JSF annotation, else * <code>false</code>. */ public static boolean isDefinedByJSFAnnotation(final ManagedBeanType managedBean) { boolean ret = false; if (managedBean != null) { final String source = getManagedBeanSource(managedBean); if (source != null && source.equals(MANAGEDBEAN_SOURCE_JSF_ANNOTATION)) { ret = true; } } return ret; } /** * Returns <code>true</code> if the managed bean is defined by a CDI annotation. * @param managedBean Managed bean instance to test. * @return <code>true</code> if the managed bean is defined by a CDI annotation, else * <code>false</code>. */ public static boolean isDefinedByCDIAnnotation(final ManagedBeanType managedBean) { boolean ret = false; if (managedBean != null) { final String source = getManagedBeanSource(managedBean); if (source != null && source.equals(MANAGEDBEAN_SOURCE_CDI_ANNOTATION)) { ret = true; } } return ret; } class ImplicitNavigationResourceProxyVisitor implements IResourceProxyVisitor { private List<IFile> files; private IContentTypeManager contentTypeMgr; private IContentType jspSourceType; private IContentType htmlSourceType; public ImplicitNavigationResourceProxyVisitor(List<IFile> files) { this.files = files; this.contentTypeMgr = Platform.getContentTypeManager(); this.jspSourceType = contentTypeMgr.getContentType("org.eclipse.jst.jsp.core.jspsource"); //$NON-NLS-1$ this.htmlSourceType = contentTypeMgr.getContentType("org.eclipse.wst.html.core.htmlsource"); //$NON-NLS-1$ } public boolean visit(IResourceProxy proxy) throws CoreException { switch (proxy.getType()) { case IResource.FOLDER: return true; case IResource.FILE: IContentType contentType = contentTypeMgr.findContentTypeFor(proxy.getName()); if (contentType != null) { if (contentType.isKindOf(jspSourceType) || contentType.isKindOf(htmlSourceType)) { files.add((IFile)proxy.requestResource()); } } break; } return false; } } }