/*
* Copyright (C) 2014 Civilian Framework.
*
* Licensed under the Civilian License (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.civilian-framework.org/license.txt
*
* 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.civilian.application;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.civilian.Application;
import org.civilian.Request;
import org.civilian.Resource;
import org.civilian.asset.AssetConfig;
import org.civilian.asset.AssetLocation;
import org.civilian.asset.AssetServices;
import org.civilian.content.ContentSerializer;
import org.civilian.content.ContentType;
import org.civilian.content.JaxbXmlSerializer;
import org.civilian.controller.ControllerFactory;
import org.civilian.controller.classloader.ReloadConfig;
import org.civilian.provider.ResponseProvider;
import org.civilian.resource.ExtensionMapping;
import org.civilian.resource.Url;
import org.civilian.text.LocaleServiceList;
import org.civilian.text.msg.MsgBundleFactories;
import org.civilian.text.msg.MsgBundleFactory;
import org.civilian.type.TypeLib;
import org.civilian.util.Check;
import org.civilian.util.IoUtil;
import org.civilian.util.Settings;
/**
* AppConfig is used during initialization of an Application.
* It is passed to the {@link Application#init(AppConfig)} method.
* Derived application classes can use the AppConfig object to configure
* the application if not already done with settings in the Civilian config file.
*/
public class AppConfig
{
/**
* Implements an extended enabled test: The value of a config entry representing
* an enabled-flag may have the values "true", "false", "develop", "production".
* The method evaluates the string and returns the boolean value of enabled flag.
* @param configEntry the value of a config entry
* @param defaultValue the default value if configEntry is null (= not specified)
* @param develop the develop flag.
*/
public static boolean isEnabled(String configEntry, boolean defaultValue, boolean develop)
{
if (configEntry == null)
return defaultValue;
if ("true".equals(configEntry))
return true;
else if ("false".equals(configEntry))
return false;
else if ("develop".equals(configEntry))
return develop;
else if ("production".equals(configEntry))
return !develop;
else
throw new IllegalArgumentException("invalid enabled-value: '" + configEntry + "'");
}
public static boolean isEnabled(Settings settings, boolean defaultValue, boolean develop)
{
return isEnabled(settings.get(ConfigKeys.ENABLED, null), defaultValue, develop);
}
/**
* Creates a new AppConfig object. This is done by the application during initialization
* when it calls Application#init(AppConfig).
*/
public AppConfig(Application app, Settings settings)
{
app_ = app;
settings_ = settings == null ? new Settings() : settings;
async_ = settings_.getBoolean(ConfigKeys.ASYNC, false);
connect_ = settings_.getBoolean(ConfigKeys.CONNECT, true);
encoding_ = settings_.get(ConfigKeys.ENCODING, ConfigKeys.ENCODING_DEFAULT);
typeLibrary_ = new TypeLib();
extensionMapping_ = app.getResourceConfig().getExtensionMapping();
defaultResExtension_ = IoUtil.normExtension(settings_.get(ConfigKeys.EXTENSION, null));
// these init calls are safe
initLocales();
initUploadConfig();
initReloadConfig();
initAssetConfig();
}
private void initLocales()
{
String[] localeTags = settings_.getList(ConfigKeys.LOCALES);
if (localeTags.length > 0)
{
supportedLocales_ = new Locale[localeTags.length];
for (int i=0; i<localeTags.length; i++)
{
// Locale.forLanguageTag does only accept tags in the form 'de-CH' not 'de_CH'
String tag = localeTags[i].replace('_', '-');
supportedLocales_[i] = Locale.forLanguageTag(tag);
}
}
else
supportedLocales_ = new Locale[] { Locale.getDefault() };
}
private void initAssetConfig()
{
assetConfig_ = new AssetConfig();
assetConfig_.setContentTypeLookup(app_.getContext().getContentTypeLookup());
}
private void initUploadConfig()
{
uploadConfig_ = new UploadConfig(new Settings(settings_, ConfigKeys.UPLOAD_PREFIX));
}
private void initReloadConfig()
{
if (app_.develop() && settings_.getBoolean(ConfigKeys.DEV_CLASSRELOAD, false))
{
Settings settings = new Settings(settings_, ConfigKeys.DEV_CLASSRELOAD + '.');
reloadConfig_ = new ReloadConfig();
reloadConfig_.includes().add(app_.getControllerConfig().getRootPackage());
reloadConfig_.excludes().add(settings.getList(ConfigKeys.EXCLUDE));
reloadConfig_.includes().add(settings.getList(ConfigKeys.INCLUDE));
}
}
/**
* Initializes all settings of the AppConfig which may throw an Exception.
*/
public void init() throws Exception
{
initAssetLocations();
initText();
}
private void initAssetLocations() throws Exception
{
Settings settings = new Settings(settings_, ConfigKeys.ASSET_PREFIX);
for (AssetLocation loc : AssetServices.getLocations(app_, settings))
assetConfig_.addLocation(loc);
}
private void initText() throws Exception
{
String tlKey = settings_.get(ConfigKeys.MESSAGES, null);
if (tlKey != null)
msgBundleFactory_ = MsgBundleFactories.createFactory(tlKey);
}
/**
* Returns the part of the application within the Civilian config file.
* The key prefix "app.<app-id>." is removed from the keys.
*/
public Settings getSettings()
{
return settings_;
}
/**
* Returns the current application version.
* The version is null by default.
* @see Application#getVersion()
*/
public String getVersion()
{
return version_;
}
/**
* Sets the application version.
* @see Application#getVersion()
*/
public void setVersion(String version)
{
version_ = version;
}
/**
* Returns the default application encoding.
* @see Application#getEncoding()
*/
public String getEncoding()
{
return encoding_;
}
/**
* Sets the default application encoding.
*/
public void setEncoding(String encoding)
{
encoding_ = Check.notNull(encoding, "encoding");
}
//----------------------------
// locale/text
//----------------------------
/**
* Sets the list of supported locales.
*/
public void setSupportedLocales(Locale... supportedLocales)
{
supportedLocales_ = Check.notEmpty(supportedLocales, "supportedLocales");
}
/**
* Returns the list of supported locales.
*/
public Locale[] getSupportedLocales()
{
return supportedLocales_;
}
/**
* Returns false, if the {@link LocaleServiceList} will only provide LocaleService
* for supported locales (and fall back to a supported locale if a LocaleService for
* any other Locale is requested), or true if LocaleServices will be returned
* for any requested locale. In the later case, unsupported LocaleServices will not be cached
* (which adds a small performance penalty).
*/
public boolean allowUnsupportedLocales()
{
return allowUnsupportedLocales_;
}
/**
* Sets if the {@link LocaleServiceList} will provide LocaleServices
* even for locales which are not included in the list of supported locales.
*/
public void setAllowUnsupportedLocales(boolean allow)
{
allowUnsupportedLocales_ = allow;
}
/**
* Returns the MsgBundleFactory used by the application to create
* locale dependent MsgBundle objects.
*/
public MsgBundleFactory getMsgBundleFactory()
{
return msgBundleFactory_;
}
/**
* Sets the MsgBundleFactory of the application.
*/
public void setMsgBundleFactory(MsgBundleFactory factory)
{
msgBundleFactory_ = factory;
}
//----------------------------
// types
//----------------------------
/**
* Returns the type library: You can modify the type library, for instance to
* add new types if needed.
*/
public TypeLib getTypeLibrary()
{
return typeLibrary_;
}
/**
* Allows to replace the type library.
*/
public void setTypeLibrary(TypeLib library)
{
typeLibrary_ = Check.notNull(library, "library");
}
//----------------------------
// assets
//----------------------------
/**
* Returns the AssetConfig. Use the AssetConfig to
* configure where your application assets are located.
*/
public AssetConfig getAssetConfig()
{
return assetConfig_;
}
//----------------------------
// reloading
//----------------------------
/**
* Returns the ReloadConfig.
* @return the reload config or null if class reloading
* is not turned on.
*/
public ReloadConfig getReloadConfig()
{
return reloadConfig_;
}
/**
* Sets the ReloadConfig.
*/
public void setReloadConfig(ReloadConfig config)
{
reloadConfig_ = config;
}
//----------------------------
// creating controllers
//----------------------------
/**
* Returns the ControllerFactory.
* @return the factory or null if no controller factory is used.
*/
public ControllerFactory getControllerFactory()
{
return controllerFactory_;
}
/**
* Sets the ControllerFactory used for creating controller objects.
*/
public void setControllerFactory(ControllerFactory factory)
{
controllerFactory_ = factory;
}
//----------------------------
// upload
//----------------------------
/**
* Returns the current UploadConfig.
*/
public UploadConfig getUploadConfig()
{
return uploadConfig_;
}
/**
* Sets the UploadConfig.
*/
public void setUploadConfig(UploadConfig uploadConfig)
{
uploadConfig_ = Check.notNull(uploadConfig, "uploadConfig");
}
//----------------------------
// resources
//----------------------------
/**
* Returns the root resource. By default the root resource
* is null. If you don't explicitly set the root resource
* the application will build the resource
* tree after initialization by scanning the classpath for
* controller classes.
*/
public Resource getRootResource()
{
return rootResource_;
}
/**
* Sets the root resource of the application. If the resource
* tree is not explicitly set, the application will build the resource
* tree after initialization by scanning the classpath for
* controller classes. If you have generated a class defining
* constants for all resource you may use its root resource to
* and avoid runtime scanning for controllers.
*/
public void setResourceRoot(Resource root)
{
Check.notNull(root, "root");
if (!root.isRoot())
throw new IllegalArgumentException("not a root: " + root);
rootResource_ = root;
}
/**
* Returns the default extension used for resource urls
* when a url is built using {@link Url#Url(ResponseProvider, Resource)}.
* @return the extension starting with a dot, or null if
* no extension should be appended to the url.
*/
public String getDefaultResExtension()
{
return defaultResExtension_;
}
/**
* Sets the default resource extension.
*/
public void setDefaultResExtension(String extension)
{
defaultResExtension_ = IoUtil.normExtension(extension);
}
/**
* Returns the ExtensionMapping object to configure extenson mappings.
* If a client is a not able to send Accept headers, extension mappings
* can be used to derive accept preferences from the URL extension.
*/
public ExtensionMapping getExtensionMapping()
{
return extensionMapping_;
}
//----------------------------
// connect
//----------------------------
/**
* Returns the connect-value.
* @see #setConnect(boolean)
*/
public boolean getConnect()
{
return connect_;
}
/**
* Sets if the application should be connected to receive requests? In case of
* of a servlet environment this means that civilian dynamically registers
* a servlet to route application requests to the application. Default is true.
*/
public void setConnect(boolean connect)
{
connect_ = connect;
}
/**
* Returns the async-value.
* @see #setAsync(boolean)
*/
public boolean getAsync()
{
return async_;
}
/**
* Sets if the application should be support asynchronous requests processing?
* @see Request#startAsync()
*/
public void setAsync(boolean async)
{
async_ = async;
}
//----------------------------
// content
//----------------------------
/**
* Returns the ContentSerializers supported by the application.
* You may add own implementations to support conversion of all
* content types needed by your application.<br>
* If you want to implement serialization of XML using JAXB, simply define
* a suitable JAXBContext, create a {@link JaxbXmlSerializer} and add it to the map.<br>
*/
public Map<ContentType,ContentSerializer> getContentSerializers()
{
return contentSerializers_;
}
/**
* Registers a ContentSerializer for a ContentType.
*/
public void registerContentSerializer(ContentType contentType, ContentSerializer serializer)
{
Check.notNull(contentType, "contentType");
Check.notNull(serializer, "serializer");
contentSerializers_.put(contentType, serializer);
}
/**
* Returns the ContentSerializer registered for a ContentType.
*/
public ContentSerializer getContentSerializer(ContentType contentType)
{
return contentSerializers_.get(contentType);
}
private Application app_;
private String encoding_;
private String version_;
private Settings settings_;
private Locale[] supportedLocales_;
private boolean allowUnsupportedLocales_;
private MsgBundleFactory msgBundleFactory_;
private TypeLib typeLibrary_;
private AssetConfig assetConfig_;
private UploadConfig uploadConfig_;
private Resource rootResource_;
private String defaultResExtension_;
private ExtensionMapping extensionMapping_;
private ReloadConfig reloadConfig_;
private boolean connect_;
private boolean async_;
private ControllerFactory controllerFactory_;
private Map<ContentType,ContentSerializer> contentSerializers_ = new HashMap<>();
}