/*******************************************************************************
* Copyright (c) 2012-2016 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.everrest.core.impl;
import org.everrest.core.ApplicationContext;
import org.everrest.core.DependencySupplier;
import org.everrest.core.GenericContainerRequest;
import org.everrest.core.GenericContainerResponse;
import org.everrest.core.Lifecycle;
import org.everrest.core.RequestHandler;
import org.everrest.core.ResourceBinder;
import org.everrest.core.impl.method.MethodInvokerDecoratorFactory;
import org.everrest.core.impl.uri.UriComponent;
import org.everrest.core.util.Tracer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Application;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.everrest.core.ApplicationContext.anApplicationContext;
import static org.everrest.core.ExtHttpHeaders.X_HTTP_METHOD_OVERRIDE;
import static org.everrest.core.impl.EverrestConfiguration.METHOD_INVOKER_DECORATOR_FACTORY;
/**
* @author andrew00x
*/
public class EverrestProcessor implements Lifecycle {
private static final Logger LOG = LoggerFactory.getLogger(EverrestProcessor.class);
private final DependencySupplier dependencySupplier;
private final RequestHandler requestHandler;
private final EverrestApplication everrestApplication;
private final EverrestConfiguration configuration;
private final MethodInvokerDecoratorFactory methodInvokerDecoratorFactory;
/**
* Application properties. Properties from this map will be copied to ApplicationContext and may be accessible via method {@link ApplicationContext#getProperties()}.
*/
private final Map<String, String> properties;
public EverrestProcessor(DependencySupplier dependencySupplier, RequestHandler requestHandler) {
this(null, dependencySupplier, requestHandler, null);
}
public EverrestProcessor(EverrestConfiguration configuration, DependencySupplier dependencySupplier, RequestHandler requestHandler, Application application) {
this.configuration = configuration == null ? new EverrestConfiguration() : configuration;
this.dependencySupplier = dependencySupplier;
this.requestHandler = requestHandler;
properties = new ConcurrentHashMap<>();
everrestApplication = new EverrestApplication();
if (application != null) {
addApplication(application);
}
methodInvokerDecoratorFactory = createMethodInvokerDecoratorFactory(this.configuration);
}
private MethodInvokerDecoratorFactory createMethodInvokerDecoratorFactory(EverrestConfiguration configuration) {
String decoratorFactoryClassName = configuration.getProperty(METHOD_INVOKER_DECORATOR_FACTORY);
if (decoratorFactoryClassName != null) {
try {
Class<?> decoratorFactoryClass = Thread.currentThread().getContextClassLoader().loadClass(decoratorFactoryClassName);
return MethodInvokerDecoratorFactory.class.cast(decoratorFactoryClass.newInstance());
} catch (Exception e) {
throw new IllegalStateException(String.format("Cannot instantiate '%s', : %s", decoratorFactoryClassName, e), e);
}
}
return null;
}
public String getProperty(String name) {
return properties.get(name);
}
public void setProperty(String name, String value) {
if (value == null) {
properties.remove(name);
} else {
properties.put(name, value);
}
}
public void process(GenericContainerRequest request, GenericContainerResponse response, EnvironmentContext environmentContext)
throws IOException {
EnvironmentContext.setCurrent(environmentContext);
ApplicationContext context = anApplicationContext()
.withRequest(request)
.withResponse(response)
.withProviders(requestHandler.getProviders())
.withProperties(properties)
.withApplication(everrestApplication)
.withConfiguration(new EverrestConfiguration(configuration))
.withDependencySupplier(dependencySupplier)
.withMethodInvokerDecoratorFactory(methodInvokerDecoratorFactory)
.build();
try {
context.start();
ApplicationContext.setCurrent(context);
if (configuration.isNormalizeUri()) {
normalizeRequestUri(request);
}
if (configuration.isHttpMethodOverride()) {
overrideHttpMethod(request);
}
requestHandler.handleRequest(request, response);
} finally {
try {
context.stop();
} finally {
ApplicationContext.setCurrent(null);
}
EnvironmentContext.setCurrent(null);
}
}
private void normalizeRequestUri(GenericContainerRequest request) {
request.setUris(UriComponent.normalize(request.getRequestUri()), request.getBaseUri());
}
private void overrideHttpMethod(GenericContainerRequest request) {
String method = request.getRequestHeaders().getFirst(X_HTTP_METHOD_OVERRIDE);
if (method != null) {
if (Tracer.isTracingEnabled()) {
Tracer.trace("Override HTTP method from \"X-HTTP-Method-Override\" header %s => %s", request.getMethod(), method);
}
request.setMethod(method);
}
}
public void addApplication(Application application) {
checkNotNull(application);
everrestApplication.addApplication(application);
ApplicationPublisher applicationPublisher = new ApplicationPublisher(requestHandler.getResources(), requestHandler.getProviders());
applicationPublisher.publish(application);
}
@Override
public void start() {
}
@Override
public void stop() {
for (Object singleton : everrestApplication.getSingletons()) {
try {
new LifecycleComponent(singleton).destroy();
} catch (InternalException e) {
LOG.error("Unable to destroy component", e);
}
}
}
public ProviderBinder getProviders() {
return requestHandler.getProviders();
}
public ResourceBinder getResources() {
return requestHandler.getResources();
}
public EverrestApplication getApplication() {
return everrestApplication;
}
public DependencySupplier getDependencySupplier() {
return dependencySupplier;
}
}