org.glassfish.jersey.server.internal.inject.WebTargetValueFactoryProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jersey-all Show documentation
Show all versions of jersey-all Show documentation
jersey-all is a rebundled verison of Jersey as one OSGi bundle.
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.jersey.server.internal.inject;
import java.lang.annotation.Annotation;
import java.security.AccessController;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Configurable;
import javax.ws.rs.core.Configuration;
import javax.ws.rs.core.Context;
import javax.inject.Inject;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.internal.Errors;
import org.glassfish.jersey.internal.inject.Injections;
import org.glassfish.jersey.internal.util.Producer;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.internal.util.ReflectionHelper;
import org.glassfish.jersey.internal.util.collection.Value;
import org.glassfish.jersey.internal.util.collection.Values;
import org.glassfish.jersey.server.ClientBinding;
import org.glassfish.jersey.server.ExtendedUriInfo;
import org.glassfish.jersey.server.Uri;
import org.glassfish.jersey.server.internal.LocalizationMessages;
import org.glassfish.jersey.server.model.Parameter;
import org.glassfish.jersey.uri.internal.JerseyUriBuilder;
import org.glassfish.hk2.api.ServiceLocator;
import jersey.repackaged.com.google.common.base.Function;
import jersey.repackaged.com.google.common.base.Predicate;
import jersey.repackaged.com.google.common.collect.Collections2;
import jersey.repackaged.com.google.common.collect.Maps;
/**
* Value factory provider supporting the {@link Uri} injection annotation.
*
* @author Pavel Bucek (pavel.bucek at oracle.com)
*/
final class WebTargetValueFactoryProvider extends AbstractValueFactoryProvider {
private final Configuration serverConfig;
private final ConcurrentMap> managedClients;
private static class ManagedClient {
private final Client instance;
private final String customBaseUri;
private ManagedClient(Client instance, String customBaseUri) {
this.instance = instance;
this.customBaseUri = customBaseUri;
}
}
private static class BindingModel {
public static final BindingModel EMPTY = new BindingModel(null);
/**
* Create a client binding model from a {@link ClientBinding client binding} annotation.
*
* @param binding client binding annotation.
* @return binding model representing a single client binding annotation.
*/
public static BindingModel create(Annotation binding) {
if (binding == null || binding.annotationType().getAnnotation(ClientBinding.class) == null) {
return EMPTY;
} else {
return new BindingModel(binding);
}
}
/**
* Create a client binding model from a set of {@link ClientBinding client binding}
* annotation candidates.
*
* A {@code ClientBinding} marker meta-annotation is used to select the set of binding
* annotations. Only those annotations that are annotated with the binding marker
* meta-annotation are considered as binding annotations. All other annotations are filtered
* out and ignored.
*
*
* @param bindingCandidates candidate binding annotations.
* @return composite binding representing the union of the individual binding annotations
* found among the binding candidates.
*/
public static BindingModel create(final Collection bindingCandidates) {
final Collection filtered =
Collections2.filter(bindingCandidates, new Predicate() {
@Override
public boolean apply(Annotation input) {
return input != null && input.annotationType().getAnnotation(ClientBinding.class) != null;
}
});
if (filtered.isEmpty()) {
return EMPTY;
} else if (filtered.size() > 1) {
throw new ProcessingException("Too many client binding annotations.");
} else {
return new BindingModel(filtered.iterator().next());
}
}
private final Annotation annotation;
private final Class extends Configuration> configClass;
private final boolean inheritProviders;
private final String baseUri;
private BindingModel(Annotation annotation) {
if (annotation == null) {
this.annotation = null;
this.configClass = ClientConfig.class;
this.inheritProviders = true;
this.baseUri = "";
} else {
this.annotation = annotation;
final ClientBinding cba = annotation.annotationType().getAnnotation(ClientBinding.class);
this.configClass = cba.configClass();
this.inheritProviders = cba.inheritServerProviders();
this.baseUri = cba.baseUri();
}
}
/**
* Get the client binding annotation this model represents.
*
* @return client binding annotation.
*/
public Annotation getAnnotation() {
return annotation;
}
/**
* Get the configuration class to be used.
*
* @return client configuration class to be used.
*/
public Class extends Configuration> getConfigClass() {
return configClass;
}
/**
* Check if the server-side providers should be inherited.
*
* @return {@code true} if server-side providers should be inherited, {@code false} otherwise.
*/
public boolean inheritProviders() {
return inheritProviders;
}
/**
* Get the client base URI.
*
* @return client base URI.
*/
public String baseUri() {
return baseUri;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
BindingModel that = (BindingModel) o;
return annotation != null ? annotation.equals(that.annotation) : that.annotation == null;
}
@Override
public int hashCode() {
return annotation != null ? annotation.hashCode() : 0;
}
@Override
public String toString() {
return "BindingModel{" +
"binding=" + annotation +
", configClass=" + configClass +
", inheritProviders=" + inheritProviders +
", baseUri=" + baseUri +
'}';
}
}
/**
* {@link Uri} injection resolver.
*/
static final class InjectionResolver extends ParamInjectionResolver {
/**
* Create new injection resolver.
*/
public InjectionResolver() {
super(WebTargetValueFactoryProvider.class);
}
}
private static final class WebTargetValueFactory extends AbstractContainerRequestValueFactory {
private final String uri;
private final Value client;
WebTargetValueFactory(String uri, Value client) {
this.uri = uri;
this.client = client;
}
@Override
public WebTarget provide() {
// no need for try-catch - unlike for @*Param annotations, any issues with @Uri would usually be caused
// by incorrect server code, so the default runtime exception mapping to 500 is appropriate
final ExtendedUriInfo uriInfo = getContainerRequest().getUriInfo();
final Map pathParamValues = Maps.transformValues(uriInfo.getPathParameters(),
new Function, Object>() {
@Override
public Object apply(List input) {
return input.isEmpty() ? null : input.get(0);
}
});
JerseyUriBuilder uriBuilder = new JerseyUriBuilder().uri(this.uri).resolveTemplates(pathParamValues);
final ManagedClient managedClient = client.get();
if (!uriBuilder.isAbsolute()) {
final String customBaseUri = managedClient.customBaseUri;
final String rootUri = customBaseUri.isEmpty() ? uriInfo.getBaseUri().toString() : customBaseUri;
uriBuilder = new JerseyUriBuilder().uri(rootUri).path(uriBuilder.toTemplate());
}
return managedClient.instance.target(uriBuilder);
}
}
/**
* Initialize the provider.
*
* @param locator service locator to be used for injecting into the values factory.
* @param serverConfig server-side configuration.
*/
@Inject
public WebTargetValueFactoryProvider(final ServiceLocator locator, @Context final Configuration serverConfig) {
super(null, locator, Parameter.Source.URI);
this.serverConfig = serverConfig;
this.managedClients = new ConcurrentHashMap>();
// init default client
this.managedClients.put(BindingModel.EMPTY, Values.lazy(new Value() {
@Override
public ManagedClient get() {
final Client client;
if (serverConfig == null) {
client = ClientBuilder.newClient();
} else {
ClientConfig clientConfig = new ClientConfig();
copyProviders(serverConfig, clientConfig);
client = ClientBuilder.newClient(clientConfig);
}
return new ManagedClient(client, "");
}
}));
}
private void copyProviders(Configuration source, Configurable> target) {
for (Class> c : source.getClasses()) {
target.register(c, source.getContracts(c));
}
for (Object o : source.getInstances()) {
Class> c = o.getClass();
target.register(c, source.getContracts(c));
}
}
@Override
protected AbstractContainerRequestValueFactory> createValueFactory(final Parameter parameter) {
return Errors.processWithException(new Producer>() {
@Override
public AbstractContainerRequestValueFactory> call() {
String targetUriTemplate = parameter.getSourceName();
if (targetUriTemplate == null || targetUriTemplate.length() == 0) {
// Invalid URI parameter name
Errors.warning(this, LocalizationMessages.INJECTED_WEBTARGET_URI_INVALID(targetUriTemplate));
return null;
}
final Class> rawParameterType = parameter.getRawType();
if (rawParameterType == WebTarget.class) {
final BindingModel binding = BindingModel.create(Arrays.asList(parameter.getAnnotations()));
Value client = managedClients.get(binding);
if (client == null) {
client = Values.lazy(new Value() {
@Override
public ManagedClient get() {
final String prefix = binding.getAnnotation().annotationType().getName() + ".";
final String baseUriProperty = prefix + "baseUri";
final Object bu = serverConfig.getProperty(baseUriProperty);
final String customBaseUri = (bu != null) ? bu.toString() : binding.baseUri();
final String configClassProperty = prefix + "configClass";
final ClientConfig cfg = resolveConfig(configClassProperty, binding);
final String inheritProvidersProperty = prefix + "inheritServerProviders";
if (PropertiesHelper.isProperty(serverConfig.getProperty(inheritProvidersProperty)) ||
binding.inheritProviders()) {
copyProviders(serverConfig, cfg);
}
final String propertyPrefix = prefix + "property.";
Collection clientProperties =
Collections2.filter(serverConfig.getPropertyNames(), new Predicate() {
@Override
public boolean apply(String property) {
return property.startsWith(propertyPrefix);
}
});
for (String property : clientProperties) {
cfg.property(property.substring(propertyPrefix.length()),
serverConfig.getProperty(property));
}
return new ManagedClient(ClientBuilder.newClient(cfg), customBaseUri);
}
});
final Value previous = managedClients.putIfAbsent(binding, client);
if (previous != null) {
client = previous;
}
}
return new WebTargetValueFactory(targetUriTemplate, client);
} else {
Errors.warning(this, LocalizationMessages.UNSUPPORTED_URI_INJECTION_TYPE(rawParameterType));
return null;
}
}
});
}
private ClientConfig resolveConfig(final String configClassProperty, final BindingModel binding) {
Class extends Configuration> configClass = binding.getConfigClass();
final Object _cc = serverConfig.getProperty(configClassProperty);
if (_cc != null) {
Class> cc;
if (_cc instanceof String) {
cc = AccessController.doPrivileged(ReflectionHelper.classForNamePA((String) _cc));
} else if (_cc instanceof Class) {
cc = (Class>) _cc;
} else {
cc = null; // will cause a warning
}
if (cc != null && Configuration.class.isAssignableFrom(cc)) {
configClass = cc.asSubclass(Configuration.class);
} else {
Errors.warning(this, LocalizationMessages.ILLEGAL_CLIENT_CONFIG_CLASS_PROPERTY_VALUE(
configClassProperty,
_cc,
configClass.getName()
));
}
}
final Configuration cfg = Injections.getOrCreate(getLocator(), configClass);
return (cfg instanceof ClientConfig) ? (ClientConfig) cfg : new ClientConfig().loadFrom(cfg);
}
}