/**
* Copyright 2008-2016 Qualogy Solutions B.V.
*
* 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.qualogy.qafe.bind.core.application;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.jibx.runtime.IUnmarshallingContext;
import com.qualogy.qafe.bind.PostProcessing;
import com.qualogy.qafe.bind.business.transaction.GlobalTransactions;
import com.qualogy.qafe.bind.core.messages.HasMessage;
import com.qualogy.qafe.bind.core.messages.Messages;
import com.qualogy.qafe.bind.domain.ApplicationMapping;
import com.qualogy.qafe.bind.io.FileLocation;
import com.qualogy.qafe.bind.util.InterfaceScanner;
import com.qualogy.qafe.core.application.ContainerManager;
import com.qualogy.qafe.core.application.LoadFailedException;
import com.qualogy.qafe.core.framework.business.BusinessManager;
import com.qualogy.qafe.core.framework.presentation.EventHandler;
import com.qualogy.qafe.core.script.ScriptEngineManager;
/**
*
* @author
*
*/
public class ApplicationContext implements Serializable, PostProcessing {
private static final Logger LOG = Logger.getLogger(ApplicationContext.class.getName());
private static final long serialVersionUID = 6623946831871605896L;
/**
* Private datamember to keep track of load errors. This is especially interesting when a big
* application-config, with more than one application fails. In the previous implementation when an error
* occured (it doesn't matter which of the applications) all the applications would not load also. This is
* quite tedious and not very friendly. So if an application failes to load, the rootCause message will be
* stored in this variable.
*/
private Throwable loadException;
public Throwable getLoadException() {
return loadException;
}
public final static String DEFAULT_APP_CONFIG_FILE_NAME = "application-config.xml";
// /////////// context info ///////////////////
protected String root;
protected String name;// non unique
protected ApplicationIdentifier id;// unique user defined id
protected int order;
protected String rootMenu;
public String getRootMenu() {
return rootMenu;
}
public void setRootMenu(String rootMenu) {
this.rootMenu = rootMenu;
}
// /////////// messages ///////////////////
/**
* Bind variable for application messages
*/
protected Messages messages;
// /////////// container managers ///////////////////
private EventHandler eventManager;
private BusinessManager businessManager;
// /////////// mappings ///////////////////
/**
* the final product of a merge between interpreted mappingFileLocations and assignedMappings, the public
* getApplicationMapping on this class will return this merged mapping.
*/
private ApplicationMapping mergedApplicationMapping;
/**
* List for application mapping file locations (path)
*/
protected List<FileLocation> mappingFileLocations;
public List<FileLocation> getMappingFileLocations() {
return mappingFileLocations;
}
/**
* List for concrete/assigned ApplicationMappings
*/
protected List<ApplicationMapping> concreteApplicationMappings;
// /////////// configuration ///////////////////
/**
* holder for configuration items
*/
protected Configuration applicationConfiguration;
/**
* String which can contain the filepath to the application context file this application context origins
* from
*/
private URI originAppConfigFileLocation;
protected GlobalTransactions globalTransactions;
/**
* boolean property can be used to set auto-scan-root property true saying automatic scan root directory
* if no application-mapping is set. (false means don't scan (default))
*/
protected boolean autoScanRoot;
/**
* boolean indicating to scan sub directories as well, only picked up when autoScanRoot is true
*/
protected boolean recursiveScan;
private List<String> warningMessages = new ArrayList<String>();
public List<String> getWarningMessages() {
return warningMessages;
}
/**
* private default constructor
*/
public ApplicationContext() {
super();
}
/**
* convinience method to get a configuration item from the configuration returns null if item not found
* for key or config is null
*
* @return
*/
public String getConfigurationItem(String key) {
return applicationConfiguration != null ? applicationConfiguration.get(key) : null;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addFileLocation(FileLocation fileLocation) {
if (mappingFileLocations == null)
mappingFileLocations = new ArrayList<FileLocation>();
mappingFileLocations.add(fileLocation);
}
public String getRoot() {
return root;
}
/**
* method called after binding of the context file. method takes care of merging and binding the
* application-mappings
*/
public void performPostProcessing() {
try {
ApplicationContextPostProcessHelper.postProcess(this);
} catch (LoadFailedException e) {
LOG.log(Level.WARNING, "ApplicationContext [id=" + getId() + ",name=" + getName()
+ "] failed to load", e);
handleLoadFailure(ExceptionUtils.getRootCause(e));
}
}
public void postset(IUnmarshallingContext context) {
if (this.root == null)
this.root = context.getDocumentName();
performPostProcessing();
}
public ApplicationMapping getApplicationMapping() {
return mergedApplicationMapping;
}
public BusinessManager getBusinessManager() {
return this.businessManager;
}
public EventHandler getEventHandler() {
return this.eventManager;
}
public ApplicationIdentifier getId() {
return id;
}
public void add(ContainerManager containerManager) {
if (containerManager instanceof EventHandler)
this.eventManager = (EventHandler) containerManager;
if (containerManager instanceof BusinessManager)
this.businessManager = (BusinessManager) containerManager;
}
public URI getOriginAppConfigFileLocation() {
return originAppConfigFileLocation;
}
public Messages getMessages() {
return this.messages;
}
public void destroy() {
if (this.eventManager != null)
this.eventManager.destroy(this);
if (this.businessManager != null)
this.businessManager.destroy(this);
ScriptEngineManager.getEngine(this).destroy();
}
public void init() {
if (this.eventManager != null)
this.eventManager.init(this);
if (this.businessManager != null)
this.businessManager.init(this);
fetchMessages(HasMessage.class);
ScriptEngineManager.getEngine(this).init();
}
public void setOriginAppConfigFileLocation(URI originAppConfigFileLocation) {
this.originAppConfigFileLocation = originAppConfigFileLocation;
}
public void setId(ApplicationIdentifier id) {
this.id = id;
}
public void setApplicationMapping(ApplicationMapping mapping) {
this.mergedApplicationMapping = mapping;
}
public String toString() {
return ToStringBuilder.reflectionToString(this);
}
/**
* Method to setup default settings for the ApplicationContext. This method will not overwrite existing
* settings, but will fill the ones that are null.
*/
public void setDefaults() {
if (this.applicationConfiguration == null)
this.applicationConfiguration = ConfigurationDefaults.getInstance().getAppConfiguration();
else {
this.applicationConfiguration.fillInTheBlanks(ConfigurationDefaults.getInstance()
.getAppConfiguration());
this.applicationConfiguration.fillInTheBlanks(ConfigurationDefaults.getInstance()
.getGlobalConfiguration());
}
}
public void handleLoadFailure(Throwable loadException) {
handleLoadFailure(loadException, false);
}
public void handleLoadFailure(Throwable loadException, boolean loadAnyway) {
// Set the load failure exception
if (loadException.getMessage() != null && loadException.getMessage().equals("Premature end of file.")) {
this.loadException = new Throwable("QAML file is empty/invalid. " + loadException.getMessage());
} else {
this.loadException = loadException;
}
if (!loadAnyway) {
// "Unload" the application-mapping of this context,
// the context will remain in the cluster, so when rendering it knows which one is failing
setApplicationMapping(null);
}
}
private void fetchMessages(Class<HasMessage> hasMessage) {
List<HasMessage> hasMessages = InterfaceScanner.scan(this, HasMessage.class, "com.qualogy");
if (hasMessages != null) {
for (HasMessage hasMsg : hasMessages) {
if (hasMsg.getMessages() != null) {
for (String msg : hasMsg.getMessages()) {
warningMessages.add(msg);
}
}
}
}
}
}