All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.stormpath.sdk.servlet.client.ClientLoader Maven / Gradle / Ivy

/*
 * Copyright 2014 Stormpath, Inc.
 *
 * 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.stormpath.sdk.servlet.client;

import com.stormpath.sdk.client.Client;
import com.stormpath.sdk.lang.Classes;
import com.stormpath.sdk.lang.Strings;
import com.stormpath.sdk.lang.UnknownClassException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletContext;

/**
 * A {@code ClientLoader} is responsible for loading a web application's Stormpath {@link Client} instance and making it
 * available in the {@code ServletContext} at application startup.  Once in the {@code ServletContext}, the
 * {@code Client} is available to any application component that needs to interact with Stormpath; these components can
 * can access the {@code client} instance easily, for example:
 *
 * 
 * Servlets.getClient(aServletRequest);
 * 
* *

Usage

* *

This implementation will, without any configuration at all, instantiate and use a * {@link DefaultServletContextClientFactory} to build the {@link Client} used by the application. The * {@code DefaultServletContextClientFactory} supports the following {@code context-param} options in your * {@code web.xml} configuration: {@code stormpathApiKeyFileLocation} and {@code stormpathClientAuthenticationScheme}. *

* *

{@code stormpathApiKeyFileLocation}

* *

The {@code stormpathApiKeyFileLocation} {@code context-param}, if present, allows you to specify the resource * path to the {@code apiKey.properties} file that should be used to authenticate requests to Stormpath. For example:

* *
 * <context-param>
 *     <param-name>stormpathApiKeyFileLocation</param-name>
 *     <param-value>/users/whatever/stormpath/apiKey.properties</param-value>
 * </context-param>
 * 
* *

You may load files from the filesystem, classpath, or URLs by prefixing the location path with * {@code file:}, {@code classpath:}, or {@code url:} respectively. If no prefix is present, {@code file:} * is assumed by default.

* *

If you do not specify this context-param, ${user.home}/.stormpath/apiKey.properties is assumed by * default.

* *

{@code stormpathClientAuthenticationScheme}

* *

The {@code stormpathClientAuthenticationScheme} {@code context-param}, if present, allows you to specify a * different HTTP authentication scheme than Stormpath's (very secure) default. At the moment, the only other * alternative is {@code basic}. For example:

* *
 * <context-param>
 *     <param-name>stormpathClientAuthenticationScheme</param-name>
 *     <param-value>basic</param-value>
 * </context-param>
 * 
* *

Because Stormpath's default authentication scheme is more secure than HTTP Basic authentication, it is * strongly recommended that you do not override this value unless you have to. For example, Google App Engine * users will be forced to specify {@code basic} here because GAE's outbound HTTP connection behavior interferes with * the default algorithm. Again, it is not recommended that you use {@code basic} unless you have a technical * limitation that prevents you from using Stormpath's default.

* *

If you do not specify this context-param, * sauthc1 * is assumed by default.

* *

Custom ServletContextClientFactory Implementation

* *

As mentioned above, the {@code ClientLoader} assumes the {@code DefaultServletContextClientFactory} behavior * (supporting the above two {@code context-param} options) is sufficient for many needs. If you need to build your * application's {@code Client} instance in a custom way - perhaps to enable specific * {@link com.stormpath.sdk.cache.CacheManager CacheManager} instance using a * {@link com.stormpath.sdk.client.Clients#builder() ClientBuilder} - you can implement the * {@link ServletContextClientFactory} interface yourself and specify that implementation class name as * follows:

* *
 * <context-param>
 *     <param-name>stormpathServletContextClientFactoryClass</param-name>
 *     <param-value>com.mycompany.myapp.stormpath.MyCustomServletContextClientFactory</param-value>
 * </context-param>
 * 
* *

If you do not specify this context-param, {@link DefaultServletContextClientFactory * com.stormpath.sdk.servlet.client.DefaultServletContextClientFactory} is assumed by default.

* *

Because the {@code DefaultServletContextClientFactory} implementation is basic and does not support specifying * a custom CacheManager implementation, it is recommended that you create your own * {@link ServletContextClientFactory} implementation and specify that class name here for more advanced use cases. *

* * @see ServletContextClientFactory * @see DefaultServletContextClientFactory * @since 1.0 */ public class ClientLoader { /** * Servlet Context config param for specifying the {@link Client} implementation class to use: * {@code stormpathServletContextClientFactoryClass} */ public static final String CLIENT_FACTORY_CLASS_PARAM = "stormpathServletContextClientFactoryClass"; public static final String CLIENT_ATTRIBUTE_KEY = Client.class.getName(); private static final Logger log = LoggerFactory.getLogger(ClientLoader.class); /** * Creates and initializes a Stormpath {@link Client} instance for the web application attributed to the * specified {@code ServletContext} and assigns that instance to the ServletContext for that context's lifespan. * The client instance is created by instantiating a {@link ServletContextClientFactory} implementation class, and the * class name can be specified as a {@code stormpathClientFactoryClass} context parameter. If this * parameter is not specified, the default {@link DefaultServletContextClientFactory} implementation is assumed. * * @param servletContext current servlet context * @return the new Stormpath {@code Client} instance. * @throws IllegalStateException if an existing Client has already been initialized and associated with * the specified {@code ServletContext}. */ public Client createClient(ServletContext servletContext) throws IllegalStateException { if (servletContext.getAttribute(CLIENT_ATTRIBUTE_KEY) != null) { String msg = "There is already a Stormpath client instance associated with the current ServletContext. " + "Check if you have multiple ClientLoader* definitions in your web.xml or annotation config!"; throw new IllegalStateException(msg); } servletContext.log("Initializing Stormpath client instance."); log.info("Starting Stormpath client initialization."); long startTime = System.currentTimeMillis(); try { Client client = doCreateClient(servletContext); servletContext.setAttribute(CLIENT_ATTRIBUTE_KEY,client); log.debug("Published Client as ServletContext attribute with name [{}]", CLIENT_ATTRIBUTE_KEY); if (log.isInfoEnabled()) { long elapsed = System.currentTimeMillis() - startTime; log.info("Stormpath client initialized in {} ms.", elapsed); } return client; } catch (RuntimeException ex) { log.error("Stormpath client initialization failed", ex); servletContext.setAttribute(CLIENT_ATTRIBUTE_KEY, ex); throw ex; } catch (Error err) { log.error("Stormpath client initialization failed", err); servletContext.setAttribute(CLIENT_ATTRIBUTE_KEY, err); throw err; } } /** * Return the {@link ServletContextClientFactory} implementation class to use, either the default {@link DefaultServletContextClientFactory} * or a custom class if specified. * * @param servletContext current servlet context * @return the WebEnvironment implementation class to use * @see #CLIENT_FACTORY_CLASS_PARAM * @see DefaultServletContextClientFactory */ protected Class determineClientFactoryClass(ServletContext servletContext) { String className = servletContext.getInitParameter(CLIENT_FACTORY_CLASS_PARAM); className = Strings.trimWhitespace(className); if (className != null) { try { return Classes.forName(className); } catch (UnknownClassException ex) { throw new IllegalStateException("Failed to load custom ServletContextClientFactory class [" + className + "]", ex); } } else { return DefaultServletContextClientFactory.class; } } /** * Instantiates a {@link Client} based on the specified ServletContext. *

* This implementation {@link #determineClientFactoryClass(javax.servlet.ServletContext) determines} a * {@link ServletContextClientFactory} implementation class to use. That class is instantiated, returned, and invoked. *

*

* This allows custom {@code ServletContextClientFactory} implementations to be specified via a ServletContext init-param if * desired. If not specified, the default {@link DefaultServletContextClientFactory} implementation will be used. *

* * @param sc current servlet context * @return the constructed Stormpath client instance */ protected Client doCreateClient(ServletContext sc) { Class clazz = determineClientFactoryClass(sc); if (!ServletContextClientFactory.class.isAssignableFrom(clazz)) { throw new IllegalStateException("Custom ServletContextClientFactory class [" + clazz.getName() + "] is not of required type [" + ServletContextClientFactory.class.getName() + "]"); } ServletContextClientFactory factory = (ServletContextClientFactory)Classes.newInstance(clazz); return factory.createClient(sc); } /** * Destroys the {@link Client} for the given servlet context. * * @param servletContext the ServletContext attributed to the WebSecurityManager */ public void destroyClient(ServletContext servletContext) { servletContext.log("Cleaning up Stormpath client."); servletContext.removeAttribute(CLIENT_ATTRIBUTE_KEY); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy