| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.jclouds.openstack.nova.v2_0.handlers; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Predicates.in; |
| import static com.google.common.base.Strings.emptyToNull; |
| import static com.google.common.collect.Maps.filterKeys; |
| import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream; |
| |
| import java.util.Set; |
| |
| import javax.annotation.Resource; |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| import org.jclouds.Constants; |
| import org.jclouds.date.DateCodecFactory; |
| import org.jclouds.fallbacks.HeaderToRetryAfterException; |
| import org.jclouds.http.HttpCommand; |
| import org.jclouds.http.HttpErrorHandler; |
| import org.jclouds.http.HttpResponse; |
| import org.jclouds.http.HttpResponseException; |
| import org.jclouds.logging.Logger; |
| import org.jclouds.openstack.nova.v2_0.functions.OverLimitParser; |
| import org.jclouds.rest.AuthorizationException; |
| import org.jclouds.rest.InsufficientResourcesException; |
| import org.jclouds.rest.ResourceNotFoundException; |
| import org.jclouds.rest.RetryAfterException; |
| |
| import com.google.common.base.Optional; |
| import com.google.common.base.Ticker; |
| import com.google.common.collect.ImmutableSet; |
| |
| /** |
| * This will parse and set an appropriate exception on the command object. |
| */ |
| // TODO: is there error spec someplace? let's type errors, etc. |
| @Singleton |
| public class NovaErrorHandler implements HttpErrorHandler { |
| |
| @com.google.inject.Inject(optional = true) |
| @Named(Constants.PROPERTY_LOGGER_WIRE_LOG_SENSITIVE_INFO) |
| private boolean logSensitiveInformation = false; |
| |
| @Resource |
| protected Logger logger = Logger.NULL; |
| protected final HeaderToRetryAfterException retryAfterParser; |
| protected final OverLimitParser overLimitParser; |
| |
| protected NovaErrorHandler(HeaderToRetryAfterException retryAfterParser, OverLimitParser overLimitParser) { |
| this.retryAfterParser = checkNotNull(retryAfterParser, "retryAfterParser"); |
| this.overLimitParser = checkNotNull(overLimitParser, "overLimitParser"); |
| } |
| |
| /** |
| * in current format, retryAt has a value of {@code 2012-11-14T21:51:28UTC}, which is an ISO-8601 seconds (not milliseconds) format. |
| */ |
| @Inject |
| public NovaErrorHandler(DateCodecFactory factory, OverLimitParser overLimitParser) { |
| this(HeaderToRetryAfterException.create(Ticker.systemTicker(), factory.iso8601Seconds()), overLimitParser); |
| } |
| |
| public void handleError(HttpCommand command, HttpResponse response) { |
| // it is important to always read fully and close streams |
| byte[] data = closeClientButKeepContentStream(response); |
| String content = data != null ? emptyToNull(new String(data)) : null; |
| |
| Exception exception = content != null ? new HttpResponseException(command, response, content) |
| : new HttpResponseException(command, response, logSensitiveInformation); |
| String requestLine = command.getCurrentRequest().getRequestLine(); |
| String message = content != null ? content : String.format("%s -> %s", requestLine, response.getStatusLine()); |
| switch (response.getStatusCode()) { |
| case 400: |
| if (message.indexOf("quota exceeded") != -1) |
| exception = new InsufficientResourcesException(message, exception); |
| else if (message.indexOf("has no fixed_ips") != -1) |
| exception = new IllegalStateException(message, exception); |
| else if (message.indexOf("already exists") != -1) |
| exception = new IllegalStateException(message, exception); |
| break; |
| case 401: |
| case 403: |
| exception = new AuthorizationException(message, exception); |
| break; |
| case 404: |
| if (!command.getCurrentRequest().getMethod().equals("DELETE")) { |
| exception = new ResourceNotFoundException(message, exception); |
| } |
| break; |
| case 500: |
| // this is needed as FloatingIPApi.allocateFromPool returns 500 when floating ips are over quota |
| if (command.getCurrentRequest().getMethod().equals("POST") && |
| message.indexOf("The server has either erred or is incapable of performing the requested operation.") != -1 && |
| command.getCurrentRequest().getEndpoint().getPath().indexOf("os-floating-ips") != -1) { |
| exception = new InsufficientResourcesException(message, exception); |
| } |
| break; |
| case 413: |
| if (content == null) { |
| exception = new InsufficientResourcesException(message, exception); |
| break; |
| } |
| exception = parseAndBuildRetryException(content, message, exception); |
| } |
| command.setException(exception); |
| } |
| |
| /** |
| * Build an exception from the response. If it contains the JSON payload then |
| * that is parsed to create a {@link RetryAfterException}, otherwise a |
| * {@link InsufficientResourcesException} is returned |
| * |
| */ |
| private Exception parseAndBuildRetryException(String json, String message, Exception exception) { |
| Set<String> retryFields = ImmutableSet.of("retryAfter", "retryAt"); |
| for (String value : filterKeys(overLimitParser.apply(json), in(retryFields)).values()) { |
| Optional<RetryAfterException> retryException = retryAfterParser.tryCreateRetryAfterException(exception, value); |
| if (retryException.isPresent()) |
| return retryException.get(); |
| } |
| return new InsufficientResourcesException(message, exception); |
| } |
| |
| } |