com.sun.faces.facelets.impl.DefaultFaceletFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javax.faces Show documentation
Show all versions of javax.faces Show documentation
This is the master POM file for Oracle's Implementation of the JSF 2.2 Specification.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2005-2007 The Apache Software Foundation
*
* 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.sun.faces.facelets.impl;
import com.sun.faces.RIConstants;
import com.sun.faces.context.FacesFileNotFoundException;
import java.net.MalformedURLException;
import javax.faces.FactoryFinder;
import javax.faces.component.UIComponent;
import javax.faces.view.facelets.Facelet;
import javax.faces.view.facelets.FaceletCache;
import com.sun.faces.facelets.compiler.Compiler;
import com.sun.faces.util.Cache;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.Util;
import java.io.ByteArrayInputStream;
import java.io.File;
import javax.faces.view.facelets.FaceletCacheFactory;
import javax.faces.view.facelets.FaceletHandler;
import javax.faces.view.facelets.ResourceResolver;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.el.ELException;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.view.facelets.FaceletException;
/**
* Default FaceletFactory implementation.
*
* @author Jacob Hookom
* @version $Id: DefaultFaceletFactory.java,v 1.10 2007/04/09 01:13:17 youngm
* Exp $
*/
public class DefaultFaceletFactory {
protected final static Logger log = FacesLogger.FACELETS_FACTORY.getLogger();
private Compiler compiler;
// We continue to use a ResourceResolver just in case someone
// provides a custom one. The DefaultResourceResolver simply uses
// the ResourceHandler to do its work.
private ResourceResolver resolver;
private URL baseUrl;
private long refreshPeriod;
private FaceletCache cache;
private ConcurrentMap> cachePerContract;
Cache idMappers;
// ------------------------------------------------------------ Constructors
public DefaultFaceletFactory() {
this.compiler = null;
this.resolver = null;
this.refreshPeriod = -1;
this.cache = null;
this.baseUrl = null;
}
public DefaultFaceletFactory(Compiler compiler, ResourceResolver resolver)
throws IOException {
this(compiler, resolver, -1, null);
}
public DefaultFaceletFactory(Compiler compiler,
ResourceResolver resolver,
long refreshPeriod) {
this(compiler, resolver, refreshPeriod, null);
}
public DefaultFaceletFactory(Compiler compiler,
ResourceResolver resolver,
long refreshPeriod,
FaceletCache cache) {
this.init(compiler, resolver, refreshPeriod, cache);
}
public final void init(Compiler compiler,
ResourceResolver resolver,
long refreshPeriod,
FaceletCache cache) {
Util.notNull("compiler", compiler);
Util.notNull("resolver", resolver);
this.compiler = compiler;
this.cachePerContract = new ConcurrentHashMap<>();
this.resolver = resolver;
this.baseUrl = resolver.resolveUrl("/");
this.idMappers = new Cache<>(new IdMapperFactory());
// this.location = url;
refreshPeriod = (refreshPeriod >= 0) ? refreshPeriod * 1000 : -1;
this.refreshPeriod = refreshPeriod;
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "Using ResourceResolver: {0}", resolver);
log.log(Level.FINE, "Using Refresh Period: {0}", refreshPeriod);
}
// We can cast to the FaceletCache here because we know
// that the Generics information is only used at compile time, and all cache
// implementations will be using instance factories provided by us and returning DefaultFacelet
this.cache = initCache((FaceletCache)cache);
}
private FaceletCache initCache(FaceletCache cache) {
if(cache == null) {
FaceletCacheFactory cacheFactory = (FaceletCacheFactory)
FactoryFinder.getFactory(FactoryFinder.FACELET_CACHE_FACTORY);
cache = cacheFactory.getFaceletCache();
}
// Create instance factories for the cache, so that the cache can
// create Facelets and Metadata Facelets
FaceletCache.MemberFactory faceletFactory =
new FaceletCache.MemberFactory() {
public DefaultFacelet newInstance(final URL key) throws IOException {
return createFacelet(key);
}
};
FaceletCache.MemberFactory metadataFaceletFactory =
new FaceletCache.MemberFactory() {
public DefaultFacelet newInstance(final URL key) throws IOException {
return createMetadataFacelet(key);
}
};
cache.setCacheFactories(faceletFactory, metadataFaceletFactory);
return cache;
}
/*
* (non-Javadoc)
*
* @see com.sun.facelets.FaceletFactory#getResourceResolver
*/
public ResourceResolver getResourceResolver() {
return resolver;
}
/*
* (non-Javadoc)
*
* @see com.sun.facelets.FaceletFactory#getFacelet(java.lang.String)
*/
public Facelet getFacelet(FacesContext context, String uri) throws IOException {
return this.getFacelet(context, resolveURL(uri));
}
public Facelet getMetadataFacelet(FacesContext context, String uri) throws IOException {
return this.getMetadataFacelet(context, resolveURL(uri));
}
/**
* Resolves a path based on the passed URL. If the path starts with '/', then
* resolve the path against {@link javax.faces.context.ExternalContext#getResource(java.lang.String)
* javax.faces.context.ExternalContext#getResource(java.lang.String)}.
* Otherwise create a new URL via {@link URL#URL(java.net.URL,
* java.lang.String) URL(URL, String)}.
*
* @param source base to resolve from
* @param path relative path to the source
*
* @return resolved URL
*
* @throws IOException
*/
public URL resolveURL(URL source, String path) throws IOException {
// PENDING(FCAPUTO): always go to the resolver to make resource libary contracts work with relative urls
if (path.startsWith("/")) {
URL url = this.resolver.resolveUrl(path);
if (url == null) {
throw new FacesFileNotFoundException(path
+ " Not Found in ExternalContext as a Resource");
}
return url;
} else {
return new URL(source, path);
}
}
/**
* Create a Facelet from the passed URL. This method checks if the cached
* Facelet needs to be refreshed before returning. If so, uses the passed URL
* to build a new instance;
*
* @param url source url
*
* @return Facelet instance
*
* @throws IOException
* @throws FaceletException
* @throws FacesException
* @throws ELException
*/
public Facelet getFacelet(FacesContext context, URL url) throws IOException {
Facelet result = getCache(context).getFacelet(url);
DefaultFacelet _facelet = null;
if (result instanceof DefaultFacelet) {
_facelet = (DefaultFacelet) result;
String docType = _facelet.getSavedDoctype();
if (null != docType) {
Util.saveDOCTYPEToFacesContextAttributes(docType);
}
String xmlDecl = _facelet.getSavedXMLDecl();
if (null != xmlDecl) {
Util.saveXMLDECLToFacesContextAttributes(xmlDecl);
}
}
return result;
}
public Facelet getMetadataFacelet(FacesContext context, URL url) throws IOException {
return getCache(context).getViewMetadataFacelet(url);
}
public boolean needsToBeRefreshed(URL url) {
if(!cache.isFaceletCached(url)) {
return true;
}
if (cachePerContract == null) {
return false;
}
// PENDING(FCAPUTO) not sure, if this is what we want.
for (FaceletCache faceletCache : cachePerContract.values()) {
if(!faceletCache.isFaceletCached(url)) {
return true;
}
}
return false;
}
private FaceletCache getCache(FacesContext context) {
List contracts = context.getResourceLibraryContracts();
if(!contracts.isEmpty()) {
StringBuilder builder = new StringBuilder();
for (int i=0; i faceletCache = cachePerContract.get(contractsKey);
if(faceletCache == null) {
// PENDING(FCAPUTO) we don't support com.sun.faces.config.WebConfiguration.WebContextInitParameter#FaceletCache for contracts
faceletCache = initCache(null);
cachePerContract.putIfAbsent(contractsKey, faceletCache);
faceletCache = cachePerContract.get(contractsKey);
}
return faceletCache;
}
return this.cache;
}
private URL resolveURL(String uri) throws IOException {
// PENDING(FCAPUTO) Deactivated caching for resource library contracts. If we still want to cache it, we need a cache per contract libraries list.
// But the ResourceHandler caches on his own (using ResourceManager).
URL url = this.resolveURL(this.baseUrl, uri);
if (url == null) {
throw new IOException("'" + uri + "' not found.");
}
return url;
}
public UIComponent _createComponent(FacesContext context, String taglibURI, String tagName,
Map attributes) {
// PENDING(FCAPUTO) does this work for resource library contracts? I think so.
UIComponent result = null;
Application app = context.getApplication();
ExternalContext extContext = context.getExternalContext();
File tmpDir = (File) extContext.getApplicationMap().get("javax.servlet.context.tempdir");
File tempFile = null;
OutputStreamWriter osw = null;
try {
// create a temporary file in that directory
tempFile = File.createTempFile("mojarra", ".tmp", tmpDir);
osw = new OutputStreamWriter(new FileOutputStream(tempFile), RIConstants.CHAR_ENCODING);
osw.append("");
osw.append("");
osw.append("");
osw.append(" attr : attributes.entrySet()) {
osw.append(attr.getKey()).append("=\"").append(attr.getValue().toString()).append("\"").append(" ");
}
}
String tempId = context.getViewRoot().createUniqueId(context, tagName);
osw.append(" id=\"").append(tempId).append("\" />");
osw.append("");
try {
osw.flush();
osw.close();
} catch (IOException ex) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Flushing and closing stream", ex);
}
}
URL fabricatedFaceletPage = tempFile.toURI().toURL();
Facelet f = createFacelet(fabricatedFaceletPage);
UIComponent tmp = (UIComponent)
app.createComponent("javax.faces.NamingContainer");
tmp.setId(context.getViewRoot().createUniqueId());
f.apply(context, tmp);
result = tmp.findComponent(tempId);
tmp.getChildren().clear();
osw = null;
} catch (MalformedURLException mue) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Invalid URL", mue);
}
} catch (IOException ioe) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "I/O error", ioe);
}
} finally {
if (null != osw) {
try {
osw.close();
} catch (IOException ioe) {
if (log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Closing stream", ioe);
}
}
}
if (null != tempFile) {
boolean successful = tempFile.delete();
if (!successful && log.isLoggable(Level.FINEST)) {
log.log(Level.FINEST, "Unable to delete temporary file.");
}
}
}
try {
byte [] faceletPage = "facelet".getBytes(RIConstants.CHAR_ENCODING);
ByteArrayInputStream bais = new ByteArrayInputStream(faceletPage);
} catch (UnsupportedEncodingException uee) {
if (log.isLoggable(Level.SEVERE)) {
log.log(Level.SEVERE, "Unsupported encoding when creating component for " + tagName + " in " + taglibURI,
uee);
}
}
if (null != result) {
result.setId(null);
}
return result;
}
/**
* Uses the internal Compiler reference to build a Facelet given the passed
* URL.
*
* @param url source
*
* @return a Facelet instance
*
* @throws IOException
* @throws FaceletException
* @throws FacesException
* @throws ELException
*/
private DefaultFacelet createFacelet(URL url) throws IOException {
if (log.isLoggable(Level.FINE)) {
log.fine("Creating Facelet for: " + url);
}
String escapedBaseURL = Pattern.quote(this.baseUrl.getFile());
String alias = '/' + url.getFile().replaceFirst(escapedBaseURL, "");
try {
FaceletHandler h = this.compiler.compile(url, alias);
return new DefaultFacelet(this,
this.compiler.createExpressionFactory(),
url,
alias,
h);
} catch (FileNotFoundException fnfe) {
throw new FileNotFoundException("Facelet "
+ alias
+ " not found at: "
+ url.toExternalForm());
}
}
private DefaultFacelet createMetadataFacelet(URL url) throws IOException {
if (log.isLoggable(Level.FINE)) {
log.fine("Creating Metadata Facelet for: " + url);
}
String escapedBaseURL = Pattern.quote(this.baseUrl.getFile());
String alias = '/' + url.getFile().replaceFirst(escapedBaseURL, "");
try {
FaceletHandler h = this.compiler.metadataCompile(url, alias);
return new DefaultFacelet(this,
this.compiler.createExpressionFactory(),
url,
alias,
h);
} catch (FileNotFoundException fnfe) {
throw new FileNotFoundException("Facelet "
+ alias
+ " not found at: "
+ url.toExternalForm());
}
}
public long getRefreshPeriod() {
return this.refreshPeriod;
}
// ---------------------------------------------------------- Nested Classes
private static final class IdMapperFactory implements Cache.Factory {
// ------------------------------------------ Methods from Cache.Factory
public IdMapper newInstance(String arg) throws InterruptedException {
return new IdMapper();
}
}
}