/* * * * JBoss, Home of Professional Open Source. * * Copyright 2011, Red Hat, Inc., and individual contributors * * as indicated by the @author tags. See the copyright.txt file in the * * distribution for a full listing of individual contributors. * * * * This is free software; you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; either version 2.1 of * * the License, or (at your option) any later version. * * * * This software is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this software; if not, write to the Free * * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.jboss.capedwarf.common.config; import java.io.Serializable; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Map; import java.util.logging.Logger; import com.google.appengine.api.NamespaceManager; import com.google.appengine.api.backends.BackendService; import com.google.appengine.api.utils.SystemProperty; import com.google.apphosting.api.ApiProxy; import com.google.common.base.Function; import org.jboss.capedwarf.common.io.SerializableMap; import org.jboss.capedwarf.shared.compatibility.Compatibility; import org.jboss.capedwarf.shared.config.AppEngineWebXml; import org.jboss.capedwarf.shared.config.ApplicationConfiguration; import org.jboss.capedwarf.shared.config.BackendsXml; import org.jboss.capedwarf.shared.config.CapedwarfConfiguration; import org.jboss.capedwarf.shared.config.CheckType; /** * @author <a href="mailto:marko.luksa@gmail.com">Marko Luksa</a> * @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a> */ public class CapedwarfEnvironment implements ApiProxy.Environment, Serializable, Cloneable, Function<BackendsXml.Backend, String> { private static final long serialVersionUID = 1L; public static final String DEFAULT_VERSION_HOSTNAME = "com.google.appengine.runtime.default_version_hostname"; public static final String INSTANCE_ID = "com.google.appengine.instance.id"; public static final String USER_ID_KEY = "com.google.appengine.api.users.UserService.user_id_key"; public static final String IS_FEDERATED_USER_KEY = "com.google.appengine.api.users.UserService.is_federated_user"; public static final String FEDERATED_AUTHORITY_KEY = "com.google.appengine.api.users.UserService.federated_authority"; public static final String FEDERATED_IDENTITY_KEY = "com.google.appengine.api.users.UserService.federated_identity"; /* Impl detail ... */ public static final String REQUEST_END_LISTENERS = "com.google.appengine.tools.development.request_end_listeners"; private static final String REQUEST_THREAD_FACTORY_ATTR = "com.google.appengine.api.ThreadManager.REQUEST_THREAD_FACTORY"; private static final String BACKGROUND_THREAD_FACTORY_ATTR = "com.google.appengine.api.ThreadManager.BACKGROUND_THREAD_FACTORY"; private static final String HTTPS = "https"; private static final String DELIMITER = "://"; private static final int DEFAULT_HTTP_PORT = 80; private static final long GLOBAL_TIME_LIMIT = Long.parseLong(System.getProperty("jboss.capedwarf.globalTimeLimit", "60000")); private final long requestStart; private Map<String, Object> attributes; private volatile Boolean checkGlobalTimeLimit; private String email; private boolean isAdmin; private String authDomain; private ApplicationConfiguration applicationConfiguration; private String baseApplicationUrl; private String secureBaseApplicationUrl; private String defaultVersionHostname; private String contextPath; private int counter; public CapedwarfEnvironment() { requestStart = System.currentTimeMillis(); // attributes attributes = new SerializableMap<>(); // the best we can do to keep env around for async Infinispan calls // a bit of a workaround for LocalServiceTestHelper::tearDown NPE attributes.put(REQUEST_END_LISTENERS, new ArrayList()); // add thread factory attributes.put(REQUEST_THREAD_FACTORY_ATTR, LazyThreadFactory.INSTANCE); attributes.put(BACKGROUND_THREAD_FACTORY_ATTR, LazyThreadFactory.INSTANCE); } public String apply(BackendsXml.Backend input) { return getDefaultVersionHostname(); } private boolean doCheckGlobalTimeLimit() { if (checkGlobalTimeLimit == null) { if (applicationConfiguration == null || applicationConfiguration.getCapedwarfConfiguration() == null || applicationConfiguration.getCapedwarfConfiguration().getCheckGlobalTimeLimit() == CheckType.NO) { checkGlobalTimeLimit = false; } else { CapedwarfConfiguration cc = applicationConfiguration.getCapedwarfConfiguration(); if (cc.getCheckGlobalTimeLimit() == CheckType.DYNAMIC) { Compatibility instance = Compatibility.getRawInstance(); // we could be in the middle of undeploy? checkGlobalTimeLimit = instance != null && instance.isEnabled(Compatibility.Feature.ENABLE_GLOBAL_TIME_LIMIT); } else { checkGlobalTimeLimit = true; } } } return checkGlobalTimeLimit; } public boolean isProduction() { return SystemProperty.environment.value() == SystemProperty.Environment.Value.Production; } public void checkGlobalTimeLimit() { if (doCheckGlobalTimeLimit()) { checkTimeLimit(requestStart, GLOBAL_TIME_LIMIT); } } public void checkTimeLimit(long start, long limit) { long current = System.currentTimeMillis(); long diff = current - start; if (diff > limit) throw new IllegalStateException("Execution taking too long: " + diff); } @Override public String getAppId() { return getAppEngineWebXml().getApplication(); } @Override public String getVersionId() { return getAppEngineWebXml().getVersion() + ".1"; // TODO? } @Override public String getModuleId() { return getAppEngineWebXml().getModule(); } private AppEngineWebXml getAppEngineWebXml() { return getApplicationConfiguration().getAppEngineWebXml(); } @Override public String getEmail() { return email; } @Override public boolean isLoggedIn() { return email != null; } @Override public boolean isAdmin() { return isLoggedIn() && isAdmin; } public void setAdmin(boolean admin) { isAdmin = admin; } @Override public String getAuthDomain() { return authDomain; } @Override public String getRequestNamespace() { return NamespaceManager.getGoogleAppsNamespace(); } @Override public Map<String, Object> getAttributes() { return attributes; } @Override public long getRemainingMillis() { return requestStart + GLOBAL_TIME_LIMIT - System.currentTimeMillis(); } public void setEmail(String email) { this.email = email; } public void setAuthDomain(String authDomain) { this.authDomain = authDomain; } public void setApplicationConfiguration(ApplicationConfiguration applicationConfiguration) { this.applicationConfiguration = applicationConfiguration; } public ApplicationConfiguration getApplicationConfiguration() { return applicationConfiguration; } private String getProperty(String prefix) { SystemProperty.Environment.Value env = SystemProperty.environment.value(); if (env != null) { String key = String.format(prefix + ".%s", env.value().toLowerCase()); String value = getApplicationConfiguration().getCapedwarfConfiguration().getProperties().getProperty(key); if (value != null) { return value; } } return getApplicationConfiguration().getCapedwarfConfiguration().getProperties().getProperty(prefix); } public void setBaseApplicationUrl(String scheme, String serverName, int port, String context) { String baseAppUrl = getProperty("base.app.url"); if (baseAppUrl != null) { baseApplicationUrl = baseAppUrl; defaultVersionHostname = baseAppUrl.substring(baseAppUrl.indexOf(DELIMITER) + DELIMITER.length()); } String secureAppUrl = getProperty("secure.app.url"); if (secureAppUrl != null) { secureBaseApplicationUrl = secureAppUrl; } String path = getProperty("context.path"); if (path != null) { contextPath = path; } if (contextPath == null) { contextPath = context; } if (baseApplicationUrl == null) { String sPort = (port == DEFAULT_HTTP_PORT) ? "" : ":" + port; defaultVersionHostname = serverName + sPort + contextPath; baseApplicationUrl = scheme + DELIMITER + defaultVersionHostname; } if (secureBaseApplicationUrl == null) { if (HTTPS.equals(scheme) || baseApplicationUrl.startsWith(HTTPS)) { secureBaseApplicationUrl = baseApplicationUrl; } else { secureBaseApplicationUrl = HTTPS + DELIMITER + defaultVersionHostname; } } attributes.put(DEFAULT_VERSION_HOSTNAME, defaultVersionHostname); } public String getBaseApplicationUrl() { return getBaseApplicationUrl(false); } public String getBaseApplicationUrl(boolean secureUrl) { return secureUrl ? secureBaseApplicationUrl : baseApplicationUrl; } public String getDefaultVersionHostname() { return defaultVersionHostname; } public String getContextPath() { return contextPath; } public static CapedwarfEnvironment createThreadLocalInstance() { CapedwarfEnvironment environment = getThreadLocalInstanceInternal(); if (environment == null) { environment = new CapedwarfEnvironment(); ApiProxy.setEnvironmentForCurrentThread(environment); } environment.counter++; return environment; } public static CapedwarfEnvironment getThreadLocalInstance() { final CapedwarfEnvironment environment = getThreadLocalInstanceInternal(); if (environment == null) { throw new IllegalStateException("Environment should exist!"); } return environment; } public static CapedwarfEnvironment cloneThreadLocalInstance() { return getThreadLocalInstance().clone(); } /** * Check if env already exists. * * @return true if already exists, false otherwise */ public static boolean hasThreadLocalInstance() { return (getThreadLocalInstanceInternal() != null); } public static void clearThreadLocalInstance() { CapedwarfEnvironment env = getThreadLocalInstanceInternal(); if (--env.counter == 0) { ApiProxy.clearEnvironmentForCurrentThread(); } if (env.counter < 0) { Logger.getLogger(CapedwarfEnvironment.class.getName()).warning("Negative counter: " + env.counter + " !!"); } } public static CapedwarfEnvironment setThreadLocalInstance(final CapedwarfEnvironment environment) { SecurityManager sm = System.getSecurityManager(); if (sm == null) { return setCapedwarfEnvironment(environment); } else { return AccessController.doPrivileged(new PrivilegedAction<CapedwarfEnvironment>() { @Override public CapedwarfEnvironment run() { return setCapedwarfEnvironment(environment); } }); } } private static CapedwarfEnvironment setCapedwarfEnvironment(CapedwarfEnvironment environment) { CapedwarfEnvironment previous = getThreadLocalInstanceInternal(); if (environment != null) { ApiProxy.setEnvironmentForCurrentThread(environment); } else { ApiProxy.clearEnvironmentForCurrentThread(); } return previous; } /** * This one doesn't make any check if env is null. * * @return env or null if not set */ protected static CapedwarfEnvironment getThreadLocalInstanceInternal() { return (CapedwarfEnvironment) ApiProxy.getCurrentEnvironment(); } /** * Mark env as initialized. */ public void initialized() { BackendsXml backends = getApplicationConfiguration().getBackendsXml(); if (backends != null) { // TODO -- move this to CD_JBossAS attributes.put(BackendService.DEVAPPSERVER_PORTMAPPING_KEY, backends.getAddresses(this)); } } public void setUserId(String userId) { getAttributes().put(USER_ID_KEY, userId); } @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException") @Override protected CapedwarfEnvironment clone() { CapedwarfEnvironment clone; try { clone = (CapedwarfEnvironment) super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } clone.attributes = new SerializableMap<>(attributes); return clone; } }