/*
* Copyright 2005-2007 WSO2, Inc. (http://wso2.com)
*
* 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 org.wso2.carbon.ui.deployment;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.equinox.http.helper.ContextPathServletAdaptor;
import org.eclipse.equinox.http.helper.FilterServletAdaptor;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.CarbonException;
import org.wso2.carbon.ui.BundleResourcePathRegistry;
import org.wso2.carbon.ui.BundleBasedUIResourceProvider;
import org.wso2.carbon.ui.util.UIResourceProvider;
import org.wso2.carbon.ui.deployment.beans.CarbonUIDefinitions;
import org.wso2.carbon.ui.deployment.beans.Component;
import org.wso2.carbon.ui.deployment.beans.CustomUIDefenitions;
import org.wso2.carbon.ui.deployment.beans.FileUploadExecutorConfig;
import org.wso2.carbon.ui.internal.CarbonUIServiceComponent;
import org.wso2.carbon.ui.transports.fileupload.FileUploadExecutorManager;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.net.URL;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
public class UIBundleDeployer implements BundleListener {
private static Log log = LogFactory.getLog(UIBundleDeployer.class);
private String bundleResourcePath = "/web";
private BundleContext bundleContext;
private HttpContext httpContext;
private ServiceTracker fileUploadExecManagerTracker;
private BundleBasedUIResourceProvider bundleBasedUIResourceProvider =
new BundleBasedUIResourceProvider(bundleResourcePath);
public UIResourceProvider getBundleBasedUIResourcePrvider() {
return bundleBasedUIResourceProvider;
}
public void deploy(BundleContext bundleContext, HttpContext context) {
this.bundleContext = bundleContext;
this.httpContext = context;
fileUploadExecManagerTracker = new ServiceTracker(bundleContext,
FileUploadExecutorManager.class.getName(), null);
fileUploadExecManagerTracker.open();
//When Carbon starts up with existing set of bundles which contain component.xmls,
//the bundleChanged() method does not get called. So calling processComponentXML()
//method here seems to be the only solution.
//TODO fork a new thread to do this task since this needs some processing time.
for (Bundle bundle : bundleContext.getBundles()) {
if ((bundle.getState() & (Bundle.UNINSTALLED | Bundle.INSTALLED)) == 0) {
try {
processUIBundle(bundle, CarbonConstants.ADD_UI_COMPONENT);
} catch (Exception e) {
log.error("Error occured when processing ui bundle " + bundle.getSymbolicName(), e);
}
}
}
try {
bundleContext.addServiceListener(new ServletServiceListener(),
"(objectClass=" + Servlet.class.getName() + ")");
} catch (InvalidSyntaxException e) {
log.error(e);
}
}
public void bundleChanged(BundleEvent event) {
Bundle bundle = event.getBundle();
try {
switch (event.getType()) {
//TODO need to fix here.
case BundleEvent.RESOLVED:
processUIBundle(bundle, CarbonConstants.ADD_UI_COMPONENT);
break;
case BundleEvent.UNRESOLVED:
// processUIBundle(bundle, CarbonConstants.REMOVE_UI_COMPONENT);
break;
}
} catch (Exception e) {
log.error("Error occured when processing component xml in bundle " + bundle.getSymbolicName(), e);
}
}
/**
* 1)Search for the UIBundle header
* 2)Check for UI bundle fragments - for backward compatibility
*
* @param bundle
* @param action
* @throws CarbonException
*/
private void processUIBundle(Bundle bundle, String action) throws CarbonException {
Dictionary headers = bundle.getHeaders();
String value = (String) headers.get("Carbon-Component");
if (value != null && "UIBundle".equals(value)) {
this.bundleBasedUIResourceProvider.addBundleResourcePaths(bundle);
processComponentXML(bundle, action);
} else if ((getHostBundle(bundle)) != null) {
processComponentXML(bundle, action);
}
}
/**
* Read component.xml(if any) and invoke suitable actions
*
* @param bundle
* @param action - used to determine if a menu item should be added or removed from UI. Value for action
* is determined by the bundle's lifecycle method(RESOLVED,...,etc).
*/
private void processComponentXML(Bundle bundle, String action) throws CarbonException {
if (bundleContext.getBundle() == bundle) {
return;
}
Component component = ComponentBuilder.build(bundle, bundleContext);
if (component == null) {
return;
} else {
ServiceReference reference = bundleContext.getServiceReference(CarbonUIDefinitions.class.getName());
CarbonUIDefinitions carbonUIDefinitions = null;
if (reference != null) {
carbonUIDefinitions = (CarbonUIDefinitions) bundleContext.getService(reference);
}
if (carbonUIDefinitions != null) {
if (log.isDebugEnabled()) {
log.debug("Found carbonUIDefinitions in OSGi context");
}
carbonUIDefinitions.setMenuDefinitions(component.getMenus(), action);
carbonUIDefinitions.setServletDefinitions(component.getServlets(), action);
carbonUIDefinitions.addUnauthenticatedUrls(component.getUnauthenticatedUrlList());
carbonUIDefinitions.addSkipTilesUrls(component.getSkipTilesUrlList());
carbonUIDefinitions.addHttpUrls(component.getSkipHttpsUrlList());
carbonUIDefinitions.addContexts(component.getContextsList());
} else {
if (log.isDebugEnabled()) {
log.debug("CarbonUIDefinitions is NULL. Registering new...");
}
carbonUIDefinitions = new CarbonUIDefinitions();
carbonUIDefinitions.setMenuDefinitions(component.getMenus(), action);
carbonUIDefinitions.setServletDefinitions(component.getServlets(), action);
carbonUIDefinitions.addUnauthenticatedUrls(component.getUnauthenticatedUrlList());
carbonUIDefinitions.addSkipTilesUrls(component.getSkipTilesUrlList());
carbonUIDefinitions.addHttpUrls(component.getSkipHttpsUrlList());
carbonUIDefinitions.addContexts(component.getContextsList());
bundleContext.registerService(CarbonUIDefinitions.class.getName(), carbonUIDefinitions, null);
}
}
//processing servlet definitions
if (component != null
&& component.getServlets() != null
&& component.getServlets().length > 0) {
HttpService httpService;
try {
httpService = CarbonUIServiceComponent.getHttpService();
} catch (Exception e) {
throw new CarbonException("An instance of HttpService is not available");
}
org.wso2.carbon.ui.deployment.beans.Servlet[] servletDefinitions = component.getServlets();
for (int a = 0; a < servletDefinitions.length; a++) {
org.wso2.carbon.ui.deployment.beans.Servlet servlet = servletDefinitions[a];
if (log.isTraceEnabled()) {
log.trace("Registering sevlet : " + servlet);
}
try {
Class clazz = Class.forName(servlet.getServletClass());
//TODO : allow servlet parameters to be passed
Dictionary params = new Hashtable();
httpService.registerServlet(servlet.getUrlPatten(),
(Servlet) clazz.newInstance(), params,
httpContext);
} catch (ClassNotFoundException e) {
log.error("Servlet class : " + servlet.getServletClass() + " not found.", e);
} catch (ServletException e) {
log.error("Problem registering Servlet class : " + servlet.getServletClass() + ".", e);
} catch (NamespaceException e) {
log.error("Problem registering Servlet class : " + servlet.getServletClass() + ".", e);
} catch (InstantiationException e) {
log.error("Problem registering Servlet class : " + servlet.getServletClass() + ".", e);
} catch (IllegalAccessException e) {
log.error("Problem registering Servlet class : " + servlet.getServletClass() + ".", e);
}
}
}
// processing the custom UI definitions required by Registry
if (component != null) {
ServiceReference reference =
bundleContext.getServiceReference(CustomUIDefenitions.class.getName());
CustomUIDefenitions customUIDefenitions = null;
if (reference != null) {
customUIDefenitions = (CustomUIDefenitions) bundleContext.getService(reference);
}
if (customUIDefenitions == null) {
String msg = "Custom UI defenitions service is not available.";
log.error(msg);
throw new CarbonException(msg);
}
Iterator<String> viewMediaTypes = component.getCustomViewUIMap().keySet().iterator();
while (viewMediaTypes.hasNext()) {
String mediaType = viewMediaTypes.next();
String uiPath = component.getCustomViewUIMap().get(mediaType);
if (customUIDefenitions.getCustomViewUI(mediaType) == null) {
customUIDefenitions.addCustomViewUI(mediaType, uiPath);
if (log.isDebugEnabled()) {
log.debug("Registered the custom view UI media type: " + mediaType + ", UI path: " + uiPath);
}
} else {
String msg = "Custom view UI is already registered for media type: " + mediaType +
". Custom UI with media type: " + mediaType + " and UI path: " +
uiPath + " will not be registered.";
log.error(msg);
}
}
Iterator<String> addMediaTypes = component.getCustomAddUIMap().keySet().iterator();
while (addMediaTypes.hasNext()) {
String mediaType = addMediaTypes.next();
String uiPath = component.getCustomAddUIMap().get(mediaType);
if (customUIDefenitions.getCustomAddUI(mediaType) == null) {
customUIDefenitions.addCustomAddUI(mediaType, uiPath);
if (log.isDebugEnabled()) {
log.debug("Registered the custom add UI media type: " + mediaType + ", UI path: " + uiPath);
}
} else {
String msg = "Custom add UI is already registered for media type: " + mediaType +
". Custom UI with media type: " + mediaType + " and UI path: " +
uiPath + " will not be registered.";
log.error(msg);
}
}
}
//This functionality can be moved to a new method
if (component.getFileUploadExecutorConfigs() != null
&& component.getFileUploadExecutorConfigs().length > 0) {
FileUploadExecutorManager executorManager = (FileUploadExecutorManager) fileUploadExecManagerTracker.getService();
if (executorManager == null) {
log.error("FileUploadExecutorManager service is not available");
return;
}
FileUploadExecutorConfig[] executorConfigs = component.getFileUploadExecutorConfigs();
for (FileUploadExecutorConfig executorConfig : executorConfigs) {
String[] mappingActions = executorConfig.getMappingActionList();
for (String mappingAction : mappingActions) {
if (CarbonConstants.ADD_UI_COMPONENT.equals(action)) {
executorManager.addExecutor(mappingAction, executorConfig.getFUploadExecClass());
} else if (CarbonConstants.REMOVE_UI_COMPONENT.equals(action)) {
executorManager.removeExecutor(mappingAction);
}
}
}
}
}
/**
* return the Host Bundle of the provided bundle, only if the provided bundle is a fragment
*
* @param bundle to be checked for the validity
* @return boolean value
*/
private Bundle getHostBundle(Bundle bundle) {
PackageAdmin packageAdmin;
try {
packageAdmin = CarbonUIServiceComponent.getPackageAdmin();
} catch (Exception e) {
return null;
}
//Check for a fragment host
if (packageAdmin.getBundleType(bundle) != PackageAdmin.BUNDLE_TYPE_FRAGMENT)
return null;
//If the host bundle is org.wso2.carbon.ui, the proceed, otherwise return
long uiBundleID = bundleContext.getBundle().getBundleId();
Bundle[] hostBundles = packageAdmin.getHosts(bundle);
if (hostBundles != null) {
for (Bundle hostBundle : hostBundles) {
if (hostBundle.getBundleId() == uiBundleID) {
return hostBundle;
}
}
}
return null;
}
// private void addBundleResourcePaths(Bundle bundle) {
// Enumeration entries = bundle.findEntries("web", "*", false);
// while (entries != null && entries.hasMoreElements()) {
// URL url = (URL) entries.nextElement();
// String path = url.getPath();
// if (path.endsWith("/")) {
// String bundleResourcePath = path.substring("/web/".length(), path.length() - 1);
// resourcePathRegistry.addBundleResourcePath(bundleResourcePath, bundle);
// }
// }
//
// }
public void registerServlet(Servlet servlet, String urlPattern, Dictionary params,
Dictionary servletAttrs, int event, javax.servlet.Filter associatedFilter) throws CarbonException {
HttpService httpService;
try {
httpService = CarbonUIServiceComponent.getHttpService();
} catch (Exception e) {
throw new CarbonException("An instance of HttpService is not available");
}
try {
if (event == ServiceEvent.REGISTERED) {
Servlet adaptedJspServlet = new ContextPathServletAdaptor(servlet, urlPattern);
if (associatedFilter == null) {
httpService.registerServlet(urlPattern, adaptedJspServlet, params, httpContext);
} else {
httpService.registerServlet(urlPattern,
new FilterServletAdaptor(associatedFilter, null, adaptedJspServlet), params, httpContext);
}
if (servletAttrs != null) {
for (Enumeration enm = servletAttrs.keys(); enm.hasMoreElements();) {
String key = (String) enm.nextElement();
adaptedJspServlet.getServletConfig().getServletContext().setAttribute(key, servletAttrs.get(key));
}
}
} else if (event == ServiceEvent.UNREGISTERING) {
httpService.unregister(urlPattern);
}
} catch (Exception e) {
throw new CarbonException("Error occurred while registering servlet", e);
}
}
public class ServletServiceListener implements ServiceListener {
public ServletServiceListener() {
try {
ServiceReference[] servletSRs = bundleContext.getServiceReferences(null,
"(objectClass=" + Servlet.class.getName() + ")");
if (servletSRs != null) {
for (ServiceReference sr : servletSRs) {
registerServletFromSR(sr, ServiceEvent.REGISTERED);
}
}
} catch (Exception e) {
log.error("Failed to obtain registerd services. Invalid filter Syntax.", e);
}
}
public void serviceChanged(ServiceEvent event) {
if (event.getType() == ServiceEvent.REGISTERED) {
registerServletFromSR(event.getServiceReference(), event.getType());
}
}
public void registerServletFromSR(ServiceReference sr, int event) {
Servlet servlet = (Servlet) bundleContext.getService(sr);
if (servlet == null) {
log.error("Servlet instance cannot be null");
return;
}
Object urlPatternObj = sr.getProperty(CarbonConstants.SERVLET_URL_PATTERN);
if (urlPatternObj == null || !(urlPatternObj instanceof String) || urlPatternObj.equals("")) {
log.error("URL pattern should not be null");
return;
}
String urlPattern = (String) urlPatternObj;
Object paramsObj = sr.getProperty(CarbonConstants.SERVLET_PARAMS);
if (paramsObj != null && !(paramsObj instanceof Dictionary)) {
log.error("Servlet params instances should be type of Dictionary");
return;
}
Dictionary params = (Dictionary) paramsObj;
Object attributesObj = sr.getProperty(CarbonConstants.SERVLET_ATTRIBUTES);
if (attributesObj != null && !(attributesObj instanceof Dictionary)) {
log.error("Servlet attributes instances should be type of Dictionary");
return;
}
Dictionary attributes = (Dictionary) attributesObj;
Object associatedFilterObj = sr.getProperty(CarbonConstants.ASSOCIATED_FILTER);
// use the qualified name for the Filter as it will by default conflicted with the OSGI class
javax.servlet.Filter associatedFilter = null;
if (associatedFilterObj != null) {
associatedFilter = (javax.servlet.Filter) associatedFilterObj;
}
try {
registerServlet(servlet, urlPattern, params, attributes, event, associatedFilter);
} catch (CarbonException e) {
log.error(e);
}
}
}
}