Skip to content

Commit

Permalink
Merge pull request #3 from christav/table-service-timeout
Browse files Browse the repository at this point in the history
Throwing ServiceTimeoutException on socket timeout.
  • Loading branch information
Chris Tavares committed Sep 27, 2012
2 parents d46f5be + ab94bb7 commit be72040
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sun.jersey.api.client.config.ClientConfig;

public class Configuration {

/**
* Property name for socket connection timeout used by services created with this configuration.
*/
public static final String PROPERTY_CONNECT_TIMEOUT = "com.microsoft.windowsazure.services.core.Configuration.connectTimeout";

/**
* Property name for socket read timeout used by services created with this configuration.
*/
public static final String PROPERTY_READ_TIMEOUT = "com.microsoft.windowsazure.services.core.Configuration.readTimeout";

private static Configuration instance;
Map<String, Object> properties;
Builder builder;
Expand All @@ -36,18 +44,11 @@ public class Configuration {
public Configuration() {
this.properties = new HashMap<String, Object>();
this.builder = DefaultBuilder.create();
init();
}

public Configuration(Builder builder) {
this.properties = new HashMap<String, Object>();
this.builder = builder;
init();
}

private void init() {
setProperty(ClientConfig.PROPERTY_READ_TIMEOUT, new Integer(90 * 1000));
setProperty(ClientConfig.PROPERTY_CONNECT_TIMEOUT, new Integer(90 * 1000));
}

public static Configuration getInstance() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Copyright 2012 Microsoft Corporation
*
* Licensed 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 com.microsoft.windowsazure.services.core;

/**
* Exception indicating a service operation has timed out.
*/
public class ServiceTimeoutException extends ServiceException {

private static final long serialVersionUID = 6612846403178749361L;

/**
* Construct a ServiceTimeoutException instance with default parameters.
*/
public ServiceTimeoutException() {
}

/**
* Construct a ServiceTimeoutException instance with the specified message.
*
* @param message
* Exception message
*/
public ServiceTimeoutException(String message) {
super(message);
}

/**
* Construct a ServiceTimeoutException instance with specified
* message and cause
*
* @param message
* Exception message
* @param cause
* Exception that caused this exception to occur
*/
public ServiceTimeoutException(String message, Throwable cause) {
super(message, cause);
}

/**
* Construct a ServiceTimeoutException instance with the specified cause.
*
* @param cause
* Exception that caused this exception to occur
*/
public ServiceTimeoutException(Throwable cause) {
super(cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@
* Copyright 2011 Microsoft Corporation
*
* Licensed 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
* 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.
* 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 com.microsoft.windowsazure.services.core.utils;

import java.net.SocketTimeoutException;

import com.microsoft.windowsazure.services.core.ServiceException;
import com.microsoft.windowsazure.services.core.ServiceTimeoutException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.api.client.UniformInterfaceException;
Expand All @@ -25,12 +28,16 @@ public static ServiceException process(String serviceName, ServiceException exce
Throwable cause = exception.getCause();

for (Throwable scan = cause; scan != null; scan = scan.getCause()) {
if (ServiceException.class.isAssignableFrom(scan.getClass())) {
Class scanClass = scan.getClass();
if (ServiceException.class.isAssignableFrom(scanClass)) {
return populate(exception, serviceName, (ServiceException) scan);
}
else if (UniformInterfaceException.class.isAssignableFrom(scan.getClass())) {
else if (UniformInterfaceException.class.isAssignableFrom(scanClass)) {
return populate(exception, serviceName, (UniformInterfaceException) scan);
}
else if (SocketTimeoutException.class.isAssignableFrom(scanClass)) {
return populate(exception, serviceName, (SocketTimeoutException) scan);
}
}

exception.setServiceName(serviceName);
Expand Down Expand Up @@ -83,4 +90,9 @@ static ServiceException populate(ServiceException exception, String serviceName,
return exception;
}

static ServiceException populate(ServiceException exception, String serviceName, SocketTimeoutException cause) {
ServiceTimeoutException newException = new ServiceTimeoutException(cause.getMessage(), cause);
newException.setServiceName(serviceName);
return newException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.microsoft.windowsazure.services.core.Builder;
import com.microsoft.windowsazure.services.core.Builder.Registry;
import com.microsoft.windowsazure.services.core.Configuration;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
Expand All @@ -31,20 +32,37 @@ public void register(Registry registry) {
@Override
public ClientConfig create(String profile, Builder builder, Map<String, Object> properties) {
ClientConfig clientConfig = new DefaultClientConfig();
profile = normalizeProfile(profile);

// Lower levels of the stack assume timeouts are set.
// Set default timeout on clientConfig in case user
// hasn't set it yet in their configuration

clientConfig.getProperties().put(ClientConfig.PROPERTY_CONNECT_TIMEOUT, new Integer(90 * 1000));
clientConfig.getProperties().put(ClientConfig.PROPERTY_READ_TIMEOUT, new Integer(90 * 1000));

for (Entry<String, Object> entry : properties.entrySet()) {
Object propertyValue = entry.getValue();
String propertyKey = entry.getKey();

if (propertyKey.equals(profile + Configuration.PROPERTY_CONNECT_TIMEOUT)) {
propertyKey = ClientConfig.PROPERTY_CONNECT_TIMEOUT;
}
if (propertyKey.equals(profile + Configuration.PROPERTY_READ_TIMEOUT)) {
propertyKey = ClientConfig.PROPERTY_READ_TIMEOUT;
}

// ClientConfig requires instance of Integer to properly set
// timeouts, but config file will deliver strings. Special
// case these timeout properties and convert them to Integer
// if necessary.
if (entry.getKey().equals(ClientConfig.PROPERTY_CONNECT_TIMEOUT)
|| entry.getKey().equals(ClientConfig.PROPERTY_READ_TIMEOUT)) {
if (propertyKey.equals(ClientConfig.PROPERTY_CONNECT_TIMEOUT)
|| propertyKey.equals(ClientConfig.PROPERTY_READ_TIMEOUT)) {
if (propertyValue instanceof String) {
propertyValue = Integer.valueOf((String) propertyValue);
}
}
clientConfig.getProperties().put(entry.getKey(), propertyValue);
clientConfig.getProperties().put(propertyKey, propertyValue);
}
return clientConfig;
}
Expand All @@ -69,4 +87,16 @@ public HttpURLConnectionClient create(String profile, Builder builder, Map<Strin
}
});
}

private static String normalizeProfile(String profile) {
if (profile == null) {
return "";
}

if (profile.endsWith(".")) {
return profile;
}

return profile + ".";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.microsoft.windowsazure.services.core.ExponentialRetryPolicy;
import com.microsoft.windowsazure.services.core.RetryPolicyFilter;
import com.microsoft.windowsazure.services.core.ServiceException;
import com.microsoft.windowsazure.services.core.ServiceTimeoutException;
import com.microsoft.windowsazure.services.table.models.BatchOperations;
import com.microsoft.windowsazure.services.table.models.BatchResult;
import com.microsoft.windowsazure.services.table.models.BatchResult.DeleteEntity;
Expand All @@ -47,7 +48,6 @@
import com.microsoft.windowsazure.services.table.models.QueryTablesResult;
import com.microsoft.windowsazure.services.table.models.ServiceProperties;
import com.microsoft.windowsazure.services.table.models.TableEntry;
import com.sun.jersey.api.client.config.ClientConfig;

public class TableServiceIntegrationTest extends IntegrationTestBase {
private static final String testTablesPrefix = "sdktest";
Expand Down Expand Up @@ -1146,22 +1146,25 @@ public void settingTimeoutWorks() throws Exception {
Configuration config = createConfiguration();

// Set timeout to very short to force failure
config.setProperty(ClientConfig.PROPERTY_CONNECT_TIMEOUT, new Integer(1));
config.setProperty(ClientConfig.PROPERTY_READ_TIMEOUT, new Integer(1));
config.setProperty(Configuration.PROPERTY_CONNECT_TIMEOUT, new Integer(1));
config.setProperty(Configuration.PROPERTY_READ_TIMEOUT, new Integer(1));

TableContract service = TableService.create(config);

try {
service.queryTables();
fail("Exception should have been thrown");
}
catch (ServiceException ex) {
assertNotNull(ex.getCause());
catch (ServiceTimeoutException ex) {
// No need to assert, test is if correct assertion type is thrown.
}
catch (Exception ex) {
fail("unexpected exception was thrown");
}
finally {
// Clean up timeouts, they interfere with other tests otherwise
config.getProperties().remove(ClientConfig.PROPERTY_CONNECT_TIMEOUT);
config.getProperties().remove(ClientConfig.PROPERTY_READ_TIMEOUT);
config.getProperties().remove(Configuration.PROPERTY_CONNECT_TIMEOUT);
config.getProperties().remove(Configuration.PROPERTY_READ_TIMEOUT);
}
}

Expand All @@ -1170,22 +1173,51 @@ public void settingTimeoutFromStringWorks() throws Exception {
Configuration config = createConfiguration();

// Set timeout to very short to force failure
config.setProperty(ClientConfig.PROPERTY_CONNECT_TIMEOUT, "1");
config.setProperty(ClientConfig.PROPERTY_READ_TIMEOUT, "1");
config.setProperty(Configuration.PROPERTY_CONNECT_TIMEOUT, "1");
config.setProperty(Configuration.PROPERTY_READ_TIMEOUT, "1");

TableContract service = TableService.create(config);

try {
service.queryTables();
fail("Exception should have been thrown");
}
catch (ServiceException ex) {
assertNotNull(ex.getCause());
catch (ServiceTimeoutException ex) {
// No need to assert, test is if correct assertion type is thrown.
}
catch (Exception ex) {
fail("unexpected exception was thrown");
}
finally {
// Clean up timeouts, they interfere with other tests otherwise
config.getProperties().remove(ClientConfig.PROPERTY_CONNECT_TIMEOUT);
config.getProperties().remove(ClientConfig.PROPERTY_READ_TIMEOUT);
config.getProperties().remove(Configuration.PROPERTY_CONNECT_TIMEOUT);
config.getProperties().remove(Configuration.PROPERTY_READ_TIMEOUT);
}
}

@Test
public void settingTimeoutPrefixedFromConfigWorks() throws Exception {
Configuration config = createConfiguration();

TableContract service = TableService.create("testprefix", config);

try {
service.queryTables();
fail("Exception should have been thrown");
}
catch (ServiceTimeoutException ex) {
// No need to assert, test is if correct assertion type is thrown.
}
catch (Exception ex) {
fail("unexpected exception was thrown");
}
}

@Test
public void prefixedTimeoutsGetLoaded() throws Exception {
Configuration config = createConfiguration();

assertEquals("3", config.getProperty("testprefix." + Configuration.PROPERTY_CONNECT_TIMEOUT));
assertEquals("7", config.getProperty("testprefix." + Configuration.PROPERTY_READ_TIMEOUT));
}
}
Loading

0 comments on commit be72040

Please sign in to comment.