SimpleMultiTenantAppFactory.java
/***************************************************************************
Copyright 2015 Emily Estes
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 net.metanotion.multitenant;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.metanotion.util.DictionaryMap;
import net.metanotion.util.Unknown;
import net.metanotion.web.AppFactory;
/** This is a basic implementation of the MultiTenantAppFactory interface that assumes an app server which has a
collection of "fixed" apps of possibly many different types, and the rest of the applications will be instances of
a specific application. */
public final class SimpleMultiTenantAppFactory implements MultiTenantAppFactory {
private static final Logger logger = LoggerFactory.getLogger(SimpleMultiTenantAppFactory.class);
/** The default implementation of the failure handler interface used by this class. */
public static final FailureHandler LOGGING_FAILURE_HANDLER = new FailureHandler() {
@Override public void startFailed(final String prefix, final Exception ex) {
logger.error("Instance '{}', start failed with exception: {}", prefix, ex);
}
@Override public void createFailed(final String prefix, final Exception ex) {
logger.error("Instance '{}', creation failed with exception: {}", prefix, ex);
}
@Override public void removeFailed(final String prefix, final Exception ex) {
logger.error("Instance '{}', removal failed with exception: {}", prefix, ex);
}
};
private final DictionaryMap<String,AppFactory> fixedApps;
private final String appPrefix;
private final FailureHandler err;
private final DictionaryMap<String,AppFactory> appInstances;
/** Create a new multi tenant app factory with a fixed set of "base" apps and a single app factory to generate the
rest of the instances.
@param fixedApps the base apps available at startup.
@param app The app factory for generating the rest of the instances.
@param appPrefix a common prefix to use for all newly generated instances created by the appFactory.
@param appInstances The list of appFactory instances that currently exist.
*/
public SimpleMultiTenantAppFactory(final DictionaryMap<String,AppFactory> fixedApps,
final AppFactory app,
final String appPrefix,
final Iterable<String> appInstances) {
this(fixedApps, app, appPrefix, appInstances, LOGGING_FAILURE_HANDLER);
}
private static final class ConstantListMap implements DictionaryMap<String, AppFactory> {
private final Iterable<String> appInstances;
private final AppFactory appFactory;
public ConstantListMap(final Iterable<String> appInstances, final AppFactory appFactory) {
this.appInstances = appInstances;
this.appFactory = appFactory;
}
@Override public AppFactory get(final String key) { return this.appFactory; }
@Override public Map<String,Object> flattenWithList(final Map<String,Object> map) {
for(final String key: this.appInstances) {
map.put(key, this.appFactory);
}
return map;
}
}
/** Create a new multi tenant app factory with a fixed set of "base" apps and a single app factory to generate the
rest of the instances.
@param fixedApps the base apps available at startup.
@param app The app factory for generating the rest of the instances.
@param appPrefix a common prefix to use for all newly generated instances created by the appFactory.
@param appInstances The list of appFactory instances that currently exist.
@param err Handler to notify about instance event failure.
*/
public SimpleMultiTenantAppFactory(final DictionaryMap<String,AppFactory> fixedApps,
final AppFactory app,
final String appPrefix,
final Iterable<String> appInstances,
final FailureHandler err) {
this(fixedApps, appPrefix, new ConstantListMap(appInstances, app), err);
}
/** Create a new multi tenant app factory with a fixed set of "base" apps and a single app factory to generate the
rest of the instances.
@param fixedApps the base apps available at startup.
@param appPrefix a common prefix to use for all newly generated instances created by the appFactory.
@param appInstances The dictionary of appFactory instances for the non-fixed apps that currently exist.
*/
public SimpleMultiTenantAppFactory(final DictionaryMap<String,AppFactory> fixedApps,
final String appPrefix,
final DictionaryMap<String, AppFactory> appInstances) {
this(fixedApps, appPrefix, appInstances, LOGGING_FAILURE_HANDLER);
}
/** Create a new multi tenant app factory with a fixed set of "base" apps and a single app factory to generate the
rest of the instances.
@param fixedApps the base apps available at startup.
@param appPrefix a common prefix to use for all newly generated instances created by the appFactory.
@param appInstances The dictionary of appFactory instances for the non-fixed apps that currently exist.
@param err Handler to notify about instance event failure.
*/
public SimpleMultiTenantAppFactory(final DictionaryMap<String,AppFactory> fixedApps,
final String appPrefix,
final DictionaryMap<String, AppFactory> appInstances,
final FailureHandler err) {
this.fixedApps = fixedApps;
this.appPrefix = appPrefix;
this.err = err;
this.appInstances = appInstances;
}
@Override public AppFactory appFactory(final String prefix) {
final AppFactory baseApp = fixedApps.get(prefix);
return (baseApp != null) ? baseApp : this.appInstances.get(prefix);
}
@Override public String schemaName(final String prefix) {
if((prefix.startsWith(appPrefix)) || (fixedApps.get(prefix) != null)) {
return prefix.replace("/", "");
} else {
throw new NoSuchElementException();
}
}
@Override public Iterator<String> iterator() {
final Map<String, Object> instances = this.fixedApps.flattenWithList(
this.appInstances.flattenWithList(new HashMap<String,Object>()));
return instances.keySet().iterator();
}
@Override public void startFailed(final String prefix, final Exception ex) { err.startFailed(prefix, ex); }
@Override public void createFailed(final String prefix, final Exception ex) { err.createFailed(prefix, ex); }
@Override public void removeFailed(final String prefix, final Exception ex) { err.removeFailed(prefix, ex); }
@Override public <I> I lookupInterface(Class<I> theInterface) {
if(theInterface == Unknown.class) { return (I) this; }
if(theInterface == MultiTenantAppFactory.class) { return (I) this; }
throw new RuntimeException("no suitable proxy.");
}
}