/*******************************************************************************
* Copyright (c) 2012,2015 EclipseSource and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Holger Staudacher - initial API and implementation
* ProSyst Software GmbH. - compatibility with OSGi specification 4.2 APIs
* Ivan Iliev - Performance Optimizations
******************************************************************************/
package com.eclipsesource.jaxrs.publisher.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.http.HttpService;
import com.eclipsesource.jaxrs.publisher.ApplicationConfiguration;
import com.eclipsesource.jaxrs.publisher.ServletConfiguration;
import com.eclipsesource.jaxrs.publisher.internal.ServiceContainer.ServiceHolder;
public class JAXRSConnector {
private static final String HTTP_SERVICE_PORT_PROPERTY = "org.osgi.service.http.port";
private static final String RESOURCE_HTTP_PORT_PROPERTY = "http.port";
private static final String DEFAULT_HTTP_PORT = "80";
private final Object lock = new Object();
private final ServiceContainer httpServices;
private final ServiceContainer resources;
private final Map<HttpService, JerseyContext> contextMap;
private final BundleContext bundleContext;
private final List<ServiceHolder> resourceCache;
private ServletConfiguration servletConfiguration;
private final ServiceContainer applicationConfigurations;
private Configuration configuration;
JAXRSConnector( BundleContext bundleContext ) {
this.bundleContext = bundleContext;
this.configuration = new Configuration( this );
this.httpServices = new ServiceContainer( bundleContext );
this.resources = new ServiceContainer( bundleContext );
this.contextMap = new HashMap<HttpService, JerseyContext>();
this.resourceCache = new ArrayList<ServiceHolder>();
this.applicationConfigurations = new ServiceContainer( bundleContext );
}
void updateConfiguration( Configuration configuration ) {
synchronized( lock ) {
this.configuration = configuration;
doUpdateConfiguration(configuration);
}
}
HttpService addHttpService( ServiceReference reference ) {
synchronized( lock ) {
return doAddHttpService( reference );
}
}
ServletConfiguration setServletConfiguration( ServiceReference reference ) {
if( servletConfiguration == null ) {
servletConfiguration = ( ServletConfiguration )bundleContext.getService( reference );
doUpdateServletConfiguration();
return servletConfiguration;
}
return null;
}
void unsetServletConfiguration( ServiceReference reference, ServletConfiguration service ) {
if( servletConfiguration == service ) {
servletConfiguration = null;
bundleContext.ungetService( reference );
doUpdateServletConfiguration();
}
}
ApplicationConfiguration addApplicationConfiguration( ServiceReference reference ) {
synchronized( lock ) {
ApplicationConfiguration service = ( ApplicationConfiguration )applicationConfigurations.add( reference ).getService();
doUpdateAppConfiguration();
return service;
}
}
void removeApplicationConfiguration( ServiceReference reference, ApplicationConfiguration service ) {
synchronized( lock ) {
applicationConfigurations.remove( service );
doUpdateAppConfiguration();
}
}
private void doUpdateServletConfiguration() {
ServiceHolder[] services = httpServices.getServices();
for( ServiceHolder serviceHolder : services ) {
contextMap.get( serviceHolder.getService() ).updateServletConfiguration( servletConfiguration );
}
}
private void doUpdateAppConfiguration() {
ServiceHolder[] services = httpServices.getServices();
for( ServiceHolder serviceHolder : services ) {
contextMap.get( serviceHolder.getService() ).updateAppConfiguration( applicationConfigurations );
}
}
private void doUpdateConfiguration(Configuration configuration) {
ServiceHolder[] services = httpServices.getServices();
for( ServiceHolder serviceHolder : services ) {
contextMap.get( serviceHolder.getService() ).updateConfiguration( configuration );
}
}
HttpService doAddHttpService( ServiceReference reference ) {
ServiceHolder serviceHolder = httpServices.add( reference );
HttpService service = ( HttpService )serviceHolder.getService();
contextMap.put( service, createJerseyContext( service, configuration, servletConfiguration ) );
clearCache();
return service;
}
private void clearCache() {
List<ServiceHolder> cache = new ArrayList<ServiceHolder>( resourceCache );
resourceCache.clear();
for( ServiceHolder serviceHolder : cache ) {
registerResource( serviceHolder );
}
}
void removeHttpService( HttpService service ) {
synchronized( lock ) {
doRemoveHttpService( service );
}
}
void doRemoveHttpService( HttpService service ) {
JerseyContext context = contextMap.remove( service );
if( context != null ) {
cacheFreedResources( context );
}
httpServices.remove( service );
}
private void cacheFreedResources( JerseyContext context ) {
List<Object> freeResources = context.eliminate();
for( Object resource : freeResources ) {
resourceCache.add( resources.find( resource ) );
}
}
Object addResource( ServiceReference reference ) {
synchronized( lock ) {
return doAddResource( reference );
}
}
private Object doAddResource( ServiceReference reference ) {
ServiceHolder serviceHolder = resources.add( reference );
registerResource( serviceHolder );
return serviceHolder.getService();
}
private void registerResource( ServiceHolder serviceHolder ) {
Object port = getPort( serviceHolder );
registerResource( serviceHolder, port );
}
private Object getPort( ServiceHolder serviceHolder ) {
Object port = serviceHolder.getReference().getProperty( RESOURCE_HTTP_PORT_PROPERTY );
if( port == null ) {
port = bundleContext.getProperty( HTTP_SERVICE_PORT_PROPERTY );
if( port == null ) {
port = DEFAULT_HTTP_PORT;
}
}
return port;
}
private void registerResource( ServiceHolder serviceHolder, Object port ) {
HttpService service = findHttpServiceForPort( port );
if( service != null ) {
JerseyContext jerseyContext = contextMap.get( service );
jerseyContext.addResource( serviceHolder.getService() );
} else {
cacheResource( serviceHolder );
}
}
private void cacheResource( ServiceHolder serviceHolder ) {
resourceCache.add( serviceHolder );
}
private HttpService findHttpServiceForPort( Object port ) {
ServiceHolder[] serviceHolders = httpServices.getServices();
HttpService result = null;
for( ServiceHolder serviceHolder : serviceHolders ) {
Object servicePort = getPort( serviceHolder );
if( servicePort.equals( port ) ) {
result = ( HttpService )serviceHolder.getService();
}
}
return result;
}
void removeResource( Object resource ) {
synchronized( lock ) {
doRemoveResource( resource );
}
}
private void doRemoveResource( Object resource ) {
ServiceHolder serviceHolder = resources.find( resource );
resourceCache.remove( serviceHolder );
HttpService httpService = findHttpServiceForPort( getPort( serviceHolder ) );
removeResourcesFromContext( resource, httpService );
resources.remove( resource );
}
private void removeResourcesFromContext( Object resource, HttpService httpService ) {
JerseyContext jerseyContext = contextMap.get( httpService );
if( jerseyContext != null ) {
jerseyContext.removeResource( resource );
}
}
// For testing purpose
JerseyContext createJerseyContext( HttpService service,
Configuration configuration,
ServletConfiguration servletConfiguration )
{
return new JerseyContext( service, configuration, servletConfiguration, applicationConfigurations );
}
}