blob: 45f9723d9277fa87e115c45cb40d59aab00295fe [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.cxf.dosgi.common.httpservice;
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.apache.cxf.Bus;
import org.apache.cxf.transport.http.DestinationRegistry;
import org.apache.cxf.transport.http.DestinationRegistryImpl;
import org.apache.cxf.transport.servlet.CXFNonSpringServlet;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceException;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.HttpContext;
import org.osgi.service.http.HttpService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component //
(//
name = "org.apache.cxf.dosgi.http", //
service = HttpServiceManager.class //
)
public class HttpServiceManager {
/**
* Prefix to create an absolute URL from a relative URL. See HttpServiceManager.getAbsoluteAddress
* Defaults to: http://localhost:8181
*/
public static final String KEY_HTTP_BASE = "httpBase";
public static final String KEY_CXF_SERVLET_ALIAS = "cxfServletAlias";
public static final String DEFAULT_CXF_SERVLET_ALIAS = "/cxf";
private static final Logger LOG = LoggerFactory.getLogger(HttpServiceManager.class);
private Map<Long, String> exportedAliases = Collections.synchronizedMap(new HashMap<Long, String>());
private String httpBase;
private String cxfServletAlias;
private HttpService httpService;
private BundleContext context;
@Activate
public void activate(ComponentContext compContext) {
Dictionary<String, Object> config = compContext.getProperties();
initFromConfig(config);
this.context = compContext.getBundleContext();
}
public void initFromConfig(Dictionary<String, Object> config) {
if (config == null) {
config = new Hashtable<>();
}
this.httpBase = getWithDefault(config.get(KEY_HTTP_BASE), "http://localhost:8181");
this.cxfServletAlias = getWithDefault(config.get(KEY_CXF_SERVLET_ALIAS), "/cxf");
}
private String getWithDefault(Object value, String defaultValue) {
return value == null ? defaultValue : value.toString();
}
public Bus registerServlet(Bus bus, String contextRoot, BundleContext callingContext, Long sid) {
bus.setExtension(new DestinationRegistryImpl(), DestinationRegistry.class);
CXFNonSpringServlet cxf = new CXFNonSpringServlet();
cxf.setBus(bus);
try {
HttpContext httpContext1 = httpService.createDefaultHttpContext();
HttpContext httpContext = new SecurityDelegatingHttpContext(callingContext, httpContext1);
httpService.registerServlet(contextRoot, cxf, new Hashtable<String, String>(), httpContext);
registerUnexportHook(sid, contextRoot);
LOG.info("Successfully registered CXF DOSGi servlet at " + contextRoot);
} catch (Exception e) {
throw new ServiceException("CXF DOSGi: problem registering CXF HTTP Servlet", e);
}
return bus;
}
/**
* This listens for service removal events and "un-exports" the service from the HttpService.
*
* @param sref the service reference to track
* @param alias the HTTP servlet context alias
*/
private void registerUnexportHook(Long sid, String alias) {
LOG.debug("Registering service listener for service with ID {}", sid);
String previous = exportedAliases.put(sid, alias);
if (previous != null) {
LOG.warn("Overwriting service export for service with ID {}", sid);
}
try {
Filter f = context.createFilter("(" + org.osgi.framework.Constants.SERVICE_ID + "=" + sid + ")");
if (f != null) {
context.addServiceListener(new UnregisterListener(), f.toString());
} else {
LOG.warn("Service listener could not be started. The service will not be automatically unexported.");
}
} catch (InvalidSyntaxException e) {
LOG.warn("Service listener could not be started. The service will not be automatically unexported.",
e);
}
}
public String getDefaultAddress(Class<?> type) {
return "/" + type.getName().replace('.', '/');
}
public String getAbsoluteAddress(String contextRoot, String endpointAddress) {
if (endpointAddress.startsWith("http")) {
return endpointAddress;
}
String effContextRoot = contextRoot == null ? cxfServletAlias : contextRoot;
return this.httpBase + effContextRoot + endpointAddress;
}
private final class UnregisterListener implements ServiceListener {
@Override
public void serviceChanged(ServiceEvent event) {
if (!(event.getType() == ServiceEvent.UNREGISTERING)) {
return;
}
final ServiceReference<?> sref = event.getServiceReference();
final Long sid = (Long)sref.getProperty(org.osgi.framework.Constants.SERVICE_ID);
final String alias = exportedAliases.remove(sid);
if (alias == null) {
LOG.error("Unable to unexport HTTP servlet for service class '{}',"
+ " service-id {}: no servlet alias found",
sref.getProperty(org.osgi.framework.Constants.OBJECTCLASS), sid);
return;
}
LOG.debug("Unexporting HTTP servlet for alias '{}'", alias);
try {
httpService.unregister(alias);
} catch (Exception e) {
LOG.warn("An exception occurred while unregistering service for HTTP servlet alias '{}'",
alias, e);
}
}
}
public void setContext(BundleContext context) {
this.context = context;
}
@Reference
public void setHttpService(HttpService httpService) {
this.httpService = httpService;
}
}