/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.deltaspike.jsf.spi.scope.window; import javax.annotation.PostConstruct; import javax.enterprise.context.SessionScoped; import javax.faces.context.FacesContext; import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.util.Map; import org.apache.deltaspike.core.api.projectstage.ProjectStage; import org.apache.deltaspike.core.util.ClassUtils; import org.apache.deltaspike.core.util.ExceptionUtils; import org.apache.deltaspike.jsf.api.config.JsfModuleConfig; import org.apache.deltaspike.jsf.api.config.base.JsfBaseConfig; import org.apache.deltaspike.jsf.util.ValueExpressionEvaluationInputStream; /** * <p>Default implementation of {@link ClientWindowConfig}. * By default it will use the internal <code>windowhandler.html</code></p> * * <p>You can @Specializes this class to tweak the configuration or * provide a completely new implementation as @Alternative.</p> */ @SessionScoped public class DefaultClientWindowConfig implements ClientWindowConfig { private static final long serialVersionUID = -708423418378550210L; /** * The location of the default windowhandler resource */ private static final String DEFAULT_WINDOW_HANDLER_HTML_FILE = "static/windowhandler.html"; private volatile Boolean javaScriptEnabled = null; /** * lazily initiated via {@link #getUserAgent(javax.faces.context.FacesContext)} */ private volatile String userAgent = null; /** * Contains the cached ClientWindow handler html for this session. */ private String clientWindowtml; @Inject private JsfModuleConfig jsfModuleConfig; @Inject private ProjectStage projectStage; private ClientWindowRenderMode defaultClientWindowRenderMode; private int maxWindowContextCount; @PostConstruct protected void init() { this.defaultClientWindowRenderMode = this.jsfModuleConfig.getDefaultWindowMode(); this.maxWindowContextCount = JsfBaseConfig.ScopeCustomization.WindowRestriction.MAX_COUNT; } @Override public boolean isJavaScriptEnabled() { if (javaScriptEnabled == null) { synchronized (this) { // double lock checking idiom on volatile variable works since java5 if (javaScriptEnabled == null) { // no info means that it is default -> true javaScriptEnabled = Boolean.TRUE; } } } return javaScriptEnabled; } @Override public void setJavaScriptEnabled(boolean javaScriptEnabled) { this.javaScriptEnabled = Boolean.valueOf(javaScriptEnabled); } /** * By default we use {@link ClientWindowRenderMode#LAZY} unless * we detect a bot. Use {@link org.apache.deltaspike.jsf.api.config.JsfModuleConfig#getDefaultWindowMode()} * to change this default behavior. Alternative: * Override this method to exclude other requests from getting accessed. */ @Override public ClientWindowRenderMode getClientWindowRenderMode(FacesContext facesContext) { if (!isJavaScriptEnabled()) { if (this.defaultClientWindowRenderMode != null) { return this.defaultClientWindowRenderMode; //currently mainly needed for 'DELEGATED' } return ClientWindowRenderMode.NONE; } String userAgent = getUserAgent(facesContext); if (userAgent != null && ( userAgent.indexOf("bot") >= 0 || // Googlebot, etc userAgent.indexOf("Bot") >= 0 || // BingBot, etc userAgent.indexOf("Slurp") >= 0 || // Yahoo Slurp userAgent.indexOf("Crawler") >= 0 // various other Crawlers ) ) { return ClientWindowRenderMode.NONE; } if (this.defaultClientWindowRenderMode != null) { return this.defaultClientWindowRenderMode; } return ClientWindowRenderMode.LAZY; } @Override public String getClientWindowHtml() { if (projectStage != ProjectStage.Development && clientWindowtml != null) { // use cached windowHandlerHtml except in Development return clientWindowtml; } InputStream is = ClassUtils.getClassLoader(null).getResourceAsStream(getClientWindowResourceLocation()); // wrap InputStream to evaluate EL expressions like resource includes is = new ValueExpressionEvaluationInputStream(FacesContext.getCurrentInstance(), is); StringBuffer sb = new StringBuffer(); try { byte[] buf = new byte[16 * 1024]; int bytesRead; while ((bytesRead = is.read(buf)) != -1) { String sbuf = new String(buf, 0, bytesRead); sb.append(sbuf); } } catch (IOException e) { ExceptionUtils.throwAsRuntimeException(e); } finally { try { is.close(); } catch (IOException e) { // do nothing, all fine so far } } clientWindowtml = sb.toString(); return clientWindowtml; } /** * This information will get stored as it cannot * change during the session anyway. * @return the UserAgent of the request. */ public String getUserAgent(FacesContext facesContext) { if (userAgent == null) { synchronized (this) { if (userAgent == null) { Map<String, String[]> requestHeaders = facesContext.getExternalContext().getRequestHeaderValuesMap(); if (requestHeaders != null && requestHeaders.containsKey("User-Agent")) { String[] userAgents = requestHeaders.get("User-Agent"); userAgent = userAgents.length > 0 ? userAgents[0] : null; } } } } return userAgent; } /** * Overwrite this to define your own ClientWindow handler html location. * This will get picked up as resource from the classpath. */ public String getClientWindowResourceLocation() { return DEFAULT_WINDOW_HANDLER_HTML_FILE; } @Override public int getMaxWindowContextCount() { return this.maxWindowContextCount; } @Override public boolean isClientWindowStoreWindowTreeEnabledOnLinkClick() { return true; } @Override public boolean isClientWindowStoreWindowTreeEnabledOnButtonClick() { return false; } @Override public boolean isClientWindowTokenizedRedirectEnabled() { return false; } }