I am using Solr 7.5 Server and I had used External Zookeeper.When I browse using the Solr Admin UI It ask authentication to me.
For Java Client I had used the below Code
BasicAuthSolrClientCache bs = new BasicAuthSolrClientCache("solr", "SolrRocks");
CloudSolrClient solrCloudClient = bs.getCloudSolrClient(zkHost);
solrCloudClient.setDefaultCollection("sample");
SolrInputDocument doc = new SolrInputDocument();
doc.addField("cat", "book");
doc.addField("id", "book-1");
doc.addField("name", "The Legend of the Hobbit part 1");
solrCloudClient.add(doc);
solrCloudClient.commit();
solrCloudClient.close();
BasicAuthSolrClientCache.java
public class BasicAuthSolrClientCache extends SolrClientCache {
private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final Map<String, SolrClient> solrClients = new HashMap<>();
private final String username;
private final String password;
public BasicAuthSolrClientCache(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public synchronized CloudSolrClient getCloudSolrClient(String zkHost) {
CloudSolrClient client;
if (solrClients.containsKey(zkHost)) {
client = (CloudSolrClient) solrClients.get(zkHost);
} else {
client = new CloudSolrClient.Builder()
.withZkHost(zkHost)
.withHttpClient(getHttpClient())
.build();
client.connect();
solrClients.put(zkHost, client);
}
return client;
}
#Override
public synchronized HttpSolrClient getHttpSolrClient(String host) {
HttpSolrClient client;
if (solrClients.containsKey(host)) {
client = (HttpSolrClient) solrClients.get(host);
} else {
client = new HttpSolrClient.Builder(host)
.withHttpClient(getHttpClient())
.build();
solrClients.put(host, client);
}
return client;
}
#Override
public synchronized void close() {
for(Map.Entry<String, SolrClient> entry : solrClients.entrySet()) {
try {
entry.getValue().close();
} catch (IOException e) {
log.error("Error closing SolrClient for " + entry.getKey(), e);
}
}
solrClients.clear();
}
private HttpClient getHttpClient() {
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new
UsernamePasswordCredentials(this.username, this.password);
provider.setCredentials(AuthScope.ANY, credentials);
return
HttpClientBuilder.create().setDefaultCredentialsProvider(provider).
build();
}
}
But it give the exception like the below,
Exception in thread "main" org.apache.solr.client.solrj.impl.CloudSolrClient$RouteException: IOException occured when talking to server at: http://192.168.0.104:8983/solr/gettingstarted_shard2_replica1 at
How to authenticate SolrCloud using SolrJ
I found a easy way to do this.
You add a request interceptor like this so you do not have to worry about creating a properly configured HttpClient instance yourself. This will just add the interceptor to the default HttpClient that Solrj creates.
org.apache.solr.client.solrj.impl.HttpClientUtil.addRequestInterceptor(new SolrPreemptiveAuthInterceptor());
The RequestInterceptor looks like this:
public class SolrPreemptiveAuthInterceptor implements HttpRequestInterceptor {
final static Logger log = LoggerFactory.getLogger(SolrPreemptiveAuthInterceptor.class);
#Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it preemptively
if (authState.getAuthScheme() == null) {
log.info("No AuthState: set Basic Auth");
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER);
Credentials creds = credsProvider.getCredentials(authScope);
if(creds == null){
log.info("No Basic Auth credentials: add them");
creds = getCredentials(authScope);
}
authState.update(new BasicScheme(), creds);
}
}
private Credentials getCredentials(AuthScope authScope) {
String user = "";
String password = "";
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user, password);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(authScope, creds);
log.info("Creating Basic Auth credentials for user {}", user);
return credsProvider.getCredentials(authScope);
}
}
You can also use UpdateRequest for indexing requests to do a basic authentication via SolrJ:
UpdateRequest ur = new UpdateRequest();
ur.add(doc);
ur.setBasicAuthCredentials("YOU USER NAME", "USER PASSWORD");
ur.setCommitWithin(COMMIT_WITHIN_INTERVAL);
ur.process(cloudSolrClient);
Related
I’m developing a small Windows form app to test Graph API functions. I have two functionalities in the application, user's log in and get channels for specified team. I created a class that contains functions for user login and for returning channels for specified team. I have a ListView on Form in which I want to show all the channels, but when I call a function for returning channels in button event, nothing happens, nothing is displayed in the ListView. Here is the code:
public static class GraphHelper
{
public static GraphServiceClient graphClient;
public static string token;
private static string[] scopes = new string[] { "user.read" };
public static string TokenForUser = null;
public static DateTimeOffset expiration;
private const string ClientId = "599ed98d-4356-4a96-ad37-04391e9c48dc";
private const string Tenant = "common"; // Alternatively "[Enter your tenant, as obtained from the Azure portal, e.g. kko365.onmicrosoft.com]"
private const string Authority = "https://login.microsoftonline.com/" + Tenant;
// The MSAL Public client app
private static IPublicClientApplication PublicClientApp;
private static string MSGraphURL = "https://graph.microsoft.com/beta/";
private static AuthenticationResult authResult;
public static GraphServiceClient GetGraphClient(string token)
{
if (graphClient == null)
{
// Create Microsoft Graph client.
try
{
graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
// This header has been added to identify our sample in the Microsoft Graph service. If extracting this code for your project please remove.
requestMessage.Headers.Add("SampleID", "uwp-csharp-snippets-sample");
}));
return graphClient;
}
catch (Exception ex)
{
Debug.WriteLine("Could not create a graph client: " + ex.Message);
}
}
return graphClient;
}
public static async Task<string> GetTokenForUserAsync()
{
if (TokenForUser == null || expiration <= DateTimeOffset.UtcNow.AddMinutes(10))
{
PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority(Authority)
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient")
.WithLogging((level, message, containsPii) =>
{
Debug.WriteLine($"MSAL: {level} {message} ");
}, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
.Build();
// It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible.
IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
IAccount firstAccount = accounts.FirstOrDefault();
try
{
authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
// A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
.ExecuteAsync()
.ConfigureAwait(false);
}
TokenForUser = authResult.AccessToken;
}
return TokenForUser;
}
public static async Task<User> GetMeAsync(string token)
{
GraphHelper.graphClient = GraphHelper.GetGraphClient(token);
try
{
// GET /me
return await GraphHelper.graphClient.Me
.Request()
.Select(u => new
{
u.DisplayName
})
.GetAsync();
}
catch (ServiceException ex)
{
Console.WriteLine($"Error getting signed-in user: {ex.Message}");
return null;
}
}
public static async Task<IEnumerable<Channel>> GetChannels(string teamId)
{
graphClient = GetGraphClient(token);
var channels = await graphClient.Teams[teamId].Channels
.Request()
.GetAsync();
return channels;
}
}
public partial class Form1 : Form
{
public static GraphServiceClient graphClient;
public static string token;
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
token = await GraphHelper.GetTokenForUserAsync();
User graphUser = await GraphHelper.GetMeAsync(token);
label2.Text = graphUser.DisplayName;
}
private async void button3_Click(object sender, EventArgs e)
{
var channels = GraphHelper.GetChannels("8557483b-a233-4710-82de-e1bdb03bb9a9").Result;
foreach (var ch in channels)
{
ListViewItem item = new ListViewItem(new string[] { ch.DisplayName, ch.Id});
listView1.Items.Add(item);
}
}
}
Does anyone how to solve this?
Try to call GraphHelper.GetChannels with await keyword on button click.
var channels = await GraphHelper.GetChannels("8557483b-a233-4710-82de-e1bdb03bb9a9");
I have a multitenant app secure with an IdentityServer4 implementation. I recently updated it to the latest ID4 and the behavior seems to have changed. Previously, I could make a request with the TokenClient inside of the IdentityModel package:
var parameters = new Dictionary<string, string>();
parameters.Add("username", loginModel.UserName);
parameters.Add("password", loginModel.Password);
var tokenClient = new TokenClient(new Uri(new Uri(accountsConfig.EndpointUrl), "/connect/token").ToString(), accountsConfig.ClientId, accountsConfig.Secret, null, AuthenticationStyle.PostValues);
var tokenResponse = await tokenClient.RequestCustomGrantAsync("AgentLogin", extra: parameters);
It would return all of the scopes defined for the client in the token. That is no longer the case. How do I configure ID4 to do that without explicitly requesting them inside of the TokenClient?
public class AgentLoginCustomGrantValidator : IExtensionGrantValidator
{
private readonly ILogger<AgentLoginCustomGrantValidator> _logger;
private readonly IAdminUserService _adminUserService;
public AgentLoginCustomGrantValidator(ILogger<AgentLoginCustomGrantValidator> logger, IAdminUserService adminUserService)
{
_logger = logger;
_adminUserService = adminUserService;
}
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
try
{
var username = context.Request.Raw.Get("username");
var password = context.Request.Raw.Get("password");
var userId = _adminUserService.AuthenticateUser(username, password);
if (userId != null)
{
var agencyUser = _adminUserService.GetUser(userId.Value);
context.Result = new GrantValidationResult($"{userId}", GrantType, agencyUser.Roles.Select(x => new Claim(JwtClaimTypes.Role, x.Name)).Concat(new List<Claim>() { new Claim(JwtClaimTypes.Name, agencyUser.UserName) { } }));
}
else
{
_logger.LogWarning($"Bum creds: {username} ");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient, "Invalid credentials");
}
}
catch (Exception ex)
{
_logger.LogError(ex.ToString());
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient, ex.Message);
}
}
public string GrantType => "AgentLogin";
}
Looks like Identity Server 4 by default only returns the requested identity or api resources for each client. However, this behaviour can be easily overridden to return all the scopes regardless whether they were requested in the token request or not. You can create a CustomClaimsService which inherits from the DefaultClaimsService.
public class CustomClaimsService : DefaultClaimsService
{
public CustomClaimsService(IProfileService profile, ILogger<DefaultClaimsService> logger) : base(profile, logger)
{
}
public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject,
Resources resources, ValidatedRequest request)
{
var baseResult = await base.GetAccessTokenClaimsAsync(subject, resources, request);
var outputClaims = baseResult.ToList();
//If there are any allowed scope claims that are not yet in the output claims - add them
foreach (var allowedClientScope in request.Client.AllowedScopes)
{
if (!outputClaims.Any(x => x.Type == JwtClaimTypes.Scope && x.Value == allowedClientScope))
{
outputClaims.Add(new Claim(JwtClaimTypes.Scope, allowedClientScope));
}
}
return outputClaims;
}
}
Then just register it with the IdentityServerBuilder service container.
var builder = services.AddIdentityServer(options =>
{
//Your identity server options
});
//Register the custom claims service with the service container
builder.Services.AddTransient<IClaimsService, CustomClaimsService>();
Each access token will now contain all the scopes that the given client is allowed.
I'm building a desktop APP using windows forms that needs to be authenticated via a WebAPI using Token authentication.
The API is proved that work because a mobile APP is using it and also I can get results using POSTMAN
The problem is when I'm calling the Authentication method from the desktop App.
When I do the request, the API recieves it and it only goes until ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context), not reaching GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) in the Auth process.
Here is my CustomAuthProvider
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "El nombre de usuario o contraseña son incorrectos");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, "JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, null);
context.Validated(ticket);
}
}
Here is my Startup class
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
ConfigureWebApi(httpConfig);
}
}
At the moment I'm trying two different ways to authenticate the APP.
First One:
public LoginResponseModel Authenticate(LoginRequestModel applicationUser)
{
using (var client = new WebClient())
{
try
{
client.Headers["Content-Type"] = "application/json";
var data = applicationUser.Serialize();
var response = client.UploadString(Context.ApiUrl + "Authenticate","POST", JsonConvert.SerializeObject(applicationUser));
var resultJson = JsonConvert.DeserializeObject<LoginResponseModel>(response);
return resultJson;
}
catch (Exception exception)
{
}
}
return null;
}
And second one:
public async Task<ApplicationUser> Authenticate(LoginRequestModel applicationUser)
{
var client = new HttpClient();
try
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
var data = applicationUser.Serialize();
var response = await client.PostAsJsonAsync(Context.ApiUrl + "Authenticate",data );
// return null by default (test)
return null;
}
catch (Exception exception)
{
}
return null;
}
And this is the model I'm using for the request
public class LoginRequestModel
{
public string Grant_type { get; set; } = "Password";
public string UserName { get; set; }
public string Password { get; set; }
}
And this should be the response:
public class LoginResponseModel
{
public string Access_token { get; set; }
public string Token_type { get; set; }
public string Expires_in { get; set; }
}
Ah the moment both ways of calling the API only reach the initial verification of the owin process (ValidateClientAuthentication). What can be happening? How I can fix this? What I need to do to make the process go to GrantResourceOwnerCredentials?
thanks for the help
I solved my problem. The problem was that the form wasn't being filled and sent correctly.
private AuthToken GetAuthToken(LoginRequestModel applicationUser)
{
using (var client = new HttpClient())
{
var form = new Dictionary<string, string>
{
{"grant_type", "password"},
{"username", applicationUser.UserName},
{"password", applicationUser.Password},
};
try
{
var tokenResponse = client.PostAsync(Context.ApiUrl + "Authenticate", new FormUrlEncodedContent(form)).Result;
var token = tokenResponse.Content.ReadAsAsync<AuthToken>(new[] { new JsonMediaTypeFormatter() }).Result;
return token;
}
catch (Exception e)
{
Log4Net.log.Error("Error Getting Auth token", e);
return null;
}
}
}
I tried doing PreEmptive Authentication for a Basic Auth protected Solr using this class https://subversion.jfrog.org/jfrog/build-info/trunk/build-info-client/src/main/java/org/jfrog/build/client/PreemptiveHttpClient.java and Solr , but the methods were Deprecated so I do not know if this was a problem. The situation is in querying is fine in Solr but for indexing I am getting a IOException occured when talking to server at: example.com:8983/solr/core1 .
The HttpSolrClient constructor requires an httpClient as a parameter to do preemptive authorization so with the class above since the httpClient is stored in a private variable I used a getter on that variable to get the httpClient and pass to the HttpSolrClient constructor. Not sure if I did that right either.
PreemptiveAuthenticate preemp = new PreemptiveAuthenticate("username", "password", 1);
DefaultHttpClient httpClient = preemp.getHttpClient();
System.out.println("Made it to connectSolr after set Authentication");
SolrClient solr = new HttpSolrClient(urlString, httpClient);
I am aware of examples like http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html Example 4.6 to do preemptive authorization with HttpClient 4.3 , but this is a test case and I do not see a way to pass the HttpClient so that I can do preemptive authentication.
Fixing Paulius's code, preemptive authentication for HttpClient 4.3. Create methods in a class the call createHttpClient when need to connect to Solr.
public static HttpClient createHttpClient(String username, String password) {
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
BasicCredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
clientBuilder.setDefaultCredentialsProvider(provider);
clientBuilder.addInterceptorFirst(new PreemptiveAuthInterceptor());
return clientBuilder.build();
}
static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
#Override
public void process (HttpRequest request, HttpContext context) throws HttpException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
Credentials credentials = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (credentials == null) {
throw new HttpException("No credentials provided for preemptive authentication.");
}
authState.update(new BasicScheme(), credentials);
}
}
}
You have to create HttpClient like this:
public static HttpClient createHttpClient(String username, String password) {
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
HttpClientBuilder clientBuilder = HttpClientBuilder.create();
BasicCredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
clientBuilder.setDefaultCredentialsProvider(provider);
clientBuilder.addInterceptorFirst((HttpRequest request, HttpContext context) -> {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
Credentials credentials = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (credentials == null) {
throw new HttpException("No credentials provided for preemptive authentication.");
}
authState.update(new BasicScheme(), credentials);
}
});
return clientBuilder.build();
}
Then you have to give it to the solr server and your requests will be authenticated preemptively.
I am trying to use OKHTTP (version 2.4.0) along retrofit (1.9.0) on google app engine (1.9.22).
Here is the how i use it:
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setConnectTimeout(COMPOSER_MODULE_CONNECTION_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setReadTimeout(COMPOSER_MODULE_SOCKET_TIMEOUT, TimeUnit.SECONDS);
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new JacksonConverter())
.setEndpoint(ENDPOINT_PATH)
.setClient(new OkClient(okHttpClient))
.build();
This throws the following error:
java.lang.NoClassDefFoundError: java.net.ProxySelector is a restricted class. Please see the Google App Engine developer's guide for more details.
at com.google.apphosting.runtime.security.shared.stub.java.net.ProxySelector.<clinit>(ProxySelector.java)
at com.squareup.okhttp.OkHttpClient.copyWithDefaults(OkHttpClient.java:614)
at com.squareup.okhttp.Call.<init>(Call.java:50)
at com.squareup.okhttp.OkHttpClient.newCall(OkHttpClient.java:595)
at retrofit.client.OkClient.execute(OkClient.java:53)
I gather from the error that "java.net.ProxySelector" is not white-listed for use on google appengine.
Question 1)
Is it possible to use OKHTTP (version 2.4.0) along retrofit (1.9.0) on google app engine (1.9.22)? i.e, is there a work around for this error
if not,
Question 2)
Are there any other way to:
(a) use async HTTP calls with google appengine (with URLFetchService, for instance) ?
(b) set connection and socket timeouts for the client used from (a) ?
The links i have come across via search:
(1) Retrofit timeout configuration for clients
(2) Google App Engine URL Fetch Java API
You can use HttpUrlConnection with Retrofit2 to use it in Google APP Engine
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.http.HttpServletResponse;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
public class RetrofitCall implements Call {
Request request;
RetrofitCall(Request request) {
this.request = request;
}
#Override
public Request request() {
return request;
}
#Override
public Response execute() throws IOException {
URL url = request.url().url();
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setRequestMethod(request.method());
Headers headers = request.headers();
if (headers != null) {
for (int i = 0; i < headers.size(); i++) {
String name = headers.name(i);
connection.setRequestProperty(name, headers.get(name));
}
}
if (request.body() != null) {
BufferedSink outbuf;
outbuf = Okio.buffer(Okio.sink(connection.getOutputStream()));
request.body().writeTo(outbuf);
outbuf.close();
}
connection.connect();
final BufferedSource source = Okio.buffer(Okio.source(connection.getInputStream()));
if (connection.getResponseCode() != HttpServletResponse.SC_OK) {
throw new IOException("Fail to call " + " :: " + source.readUtf8());
}
Response response = new Response.Builder()
.code(connection.getResponseCode())
.message(connection.getResponseMessage())
.request(request)
.protocol(Protocol.HTTP_1_1)
.body(new ResponseBody() {
#Override
public MediaType contentType() {
return MediaType.parse(connection.getContentType());
}
#Override
public long contentLength() {
return connection.getContentLengthLong();
}
#Override
public BufferedSource source() {
return source;
}
})
.build();
return response;
}
#Override
public void enqueue(Callback responseCallback) {
}
#Override
public void cancel() {
}
#Override
public boolean isExecuted() {
return false;
}
#Override
public boolean isCanceled() {
return false;
}
public static class Factory implements Call.Factory {
#Override
public Call newCall(Request request) {
return new RetrofitCall(request);
}
}
}
You can use the following code snippet to run Retorifit2 with GAE limitations. It contains a lot of debugging stuffs free to remove in production and does not implement real async call.
okhttp3.Call.Factory gaeCallFactory = new okhttp3.Call.Factory() {
#Override
public okhttp3.Call newCall(final Request request) {
final URL url = request.url().url();
final String method = url.toString();
return new okhttp3.Call() {
#Override
public Request request() {
return request;
}
#Override
public Response execute() throws IOException {
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(false);
if (request.body() != null) {
//TODO ajust for different needs
connection.setRequestProperty("Content-Type", "application/json");
connection.setDoOutput(true);
BufferedSink outbuf;
ByteArrayOutputStream out = new ByteArrayOutputStream();
outbuf = Okio.buffer(Okio.sink(out));
request.body().writeTo(outbuf);
outbuf.close();
logger.info("Calling " + method + "\n" + new String(out.toByteArray()));
outbuf = Okio.buffer(Okio.sink(connection.getOutputStream()));
request.body().writeTo(outbuf);
outbuf.close();
} else {
logger.info("Calling " + method);
}
final BufferedSource source = Okio.buffer(Okio.source(connection.getInputStream()));
if (connection.getResponseCode() != HttpServletResponse.SC_OK) {
throw new IOException("Fail to call " + method + " :: " + source.readUtf8());
}
Response response = new Response.Builder()
.code(connection.getResponseCode())
.message(connection.getResponseMessage())
.request(request)
.protocol(Protocol.HTTP_1_1)
.body(new ResponseBody() {
#Override
public MediaType contentType() {
return MediaType.parse(connection.getContentType());
}
#Override
public long contentLength() {
return connection.getContentLengthLong();
}
#Override
public BufferedSource source() {
return source;
}
})
.build();
logger.info("Call response code: " + response.code() + " message: " + response.message());
return response;
}
#Override
public void enqueue(Callback responseCallback) {
try {
responseCallback.onResponse(this, execute());
} catch (IOException e) {
responseCallback.onFailure(this, e);
}
}
#Override
public void cancel() {
}
#Override
public boolean isExecuted() {
return false;
}
#Override
public boolean isCanceled() {
return false;
}
};
}
};
Retrofit retrofit = new Retrofit.Builder()
.callFactory(gaeCallFactory)
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(ENDPOINT_URI)
.build();
You need to use the Appengine URLFetchClient instead of the OkHttpClient. Like this:
import retrofit.appengine.UrlFetchClient;
RestAdapter restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new JacksonConverter())
.setEndpoint(ENDPOINT_PATH)
.setClient(new UrlFetchClient())
.build();
Please note this only works with Retrofit1, this will not work with Retrofit2 because it's coupled directly to OkHttp as explained by Jake Wharton here