blob: 50c506ccb372d4a0c50e85e4633e128dad0551b5 [file] [log] [blame]
/*
* 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.apache.qpid.server.management.plugin.servlet.rest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Joiner;
import com.google.common.util.concurrent.ListenableFuture;
import org.apache.qpid.server.management.plugin.RequestType;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.preferences.Preference;
import org.apache.qpid.server.model.preferences.PreferenceFactory;
import org.apache.qpid.server.model.preferences.UserPreferences;
import org.apache.qpid.server.util.FutureHelper;
public class RestUserPreferenceHandler
{
private final long _preferenceOperationTimeout;
public RestUserPreferenceHandler(final long preferenceOperationTimeout)
{
_preferenceOperationTimeout = preferenceOperationTimeout;
}
public void handleDELETE(final UserPreferences userPreferences, final RequestInfo requestInfo)
{
if (userPreferences == null)
{
throw new NotFoundException("User preferences are not available");
}
final List<String> preferencesParts = requestInfo.getPreferencesParts();
final Map<String, List<String>> queryParameters = requestInfo.getQueryParameters();
UUID id = getIdFromQueryParameters(queryParameters);
String type = null;
String name = null;
if (preferencesParts.size() == 2)
{
type = preferencesParts.get(0);
name = preferencesParts.get(1);
}
else if (preferencesParts.size() == 1)
{
type = preferencesParts.get(0);
}
else if (preferencesParts.size() == 0)
{
// pass
}
else
{
throw new IllegalArgumentException(String.format("unexpected path '%s'",
Joiner.on("/").join(preferencesParts)));
}
awaitFuture(userPreferences.delete(type, name, id));
}
public void handlePUT(ConfiguredObject<?> target, RequestInfo requestInfo, Object providedObject)
{
UserPreferences userPreferences = target.getUserPreferences();
if (userPreferences == null)
{
throw new NotFoundException("User preferences are not available");
}
final List<String> preferencesParts = requestInfo.getPreferencesParts();
if (preferencesParts.size() == 2)
{
if (!(providedObject instanceof Map))
{
throw new IllegalArgumentException("expected object");
}
Map<String, Object> providedAttributes = (Map<String, Object>) providedObject;
String type = preferencesParts.get(0);
String name = preferencesParts.get(1);
ensureAttributeMatches(providedAttributes, "name", name);
ensureAttributeMatches(providedAttributes, "type", type);
Preference preference = PreferenceFactory.fromAttributes(target, providedAttributes);
awaitFuture(userPreferences.replaceByTypeAndName(type, name , preference));
}
else if (preferencesParts.size() == 1)
{
String type = preferencesParts.get(0);
if (!(providedObject instanceof List))
{
throw new IllegalArgumentException("expected a list of objects");
}
List<Preference> replacementPreferences = validateAndConvert(target, type, (List<Object>) providedObject);
awaitFuture(userPreferences.replaceByType(type, replacementPreferences));
}
else if (preferencesParts.size() == 0)
{
if (!(providedObject instanceof Map))
{
throw new IllegalArgumentException("expected object");
}
List<Preference> replacementPreferences = validateAndConvert(target, (Map<String, Object>) providedObject);
awaitFuture(userPreferences.replace(replacementPreferences));
}
else
{
throw new IllegalArgumentException(String.format("unexpected path '%s'",
Joiner.on("/").join(preferencesParts)));
}
}
public void handlePOST(ConfiguredObject<?> target, RequestInfo requestInfo, Object providedObject)
{
UserPreferences userPreferences = target.getUserPreferences();
if (userPreferences == null)
{
throw new NotFoundException("User preferences are not available");
}
final List<String> preferencesParts = requestInfo.getPreferencesParts();
final Set<Preference> preferences = new LinkedHashSet<>();
if (preferencesParts.size() == 1)
{
String type = preferencesParts.get(0);
if (!(providedObject instanceof List))
{
throw new IllegalArgumentException("expected a list of objects");
}
preferences.addAll(validateAndConvert(target, type, (List<Object>) providedObject));
}
else if (preferencesParts.size() == 0)
{
if (!(providedObject instanceof Map))
{
throw new IllegalArgumentException("expected object");
}
preferences.addAll(validateAndConvert(target, (Map<String, Object>) providedObject));
}
else
{
throw new IllegalArgumentException(String.format("unexpected path '%s'",
Joiner.on("/").join(preferencesParts)));
}
awaitFuture(userPreferences.updateOrAppend(preferences));
}
public Object handleGET(UserPreferences userPreferences, RequestInfo requestInfo)
{
if (userPreferences == null)
{
throw new NotFoundException("User preferences are not available");
}
final List<String> preferencesParts = requestInfo.getPreferencesParts();
final Map<String, List<String>> queryParameters = requestInfo.getQueryParameters();
UUID id = getIdFromQueryParameters(queryParameters);
final ListenableFuture<Set<Preference>> allPreferencesFuture;
if (requestInfo.getType() == RequestType.USER_PREFERENCES)
{
allPreferencesFuture = userPreferences.getPreferences();
}
else if (requestInfo.getType() == RequestType.VISIBLE_PREFERENCES)
{
allPreferencesFuture = userPreferences.getVisiblePreferences();
}
else
{
throw new IllegalStateException(String.format(
"RestUserPreferenceHandler called with a unsupported request type: %s", requestInfo.getType()));
}
final Set<Preference> allPreferences;
allPreferences = awaitFuture(allPreferencesFuture);
if (preferencesParts.size() == 2)
{
String type = preferencesParts.get(0);
String name = preferencesParts.get(1);
Preference foundPreference = null;
for (Preference preference : allPreferences)
{
if (preference.getType().equals(type) && preference.getName().equals(name))
{
if (id == null || id.equals(preference.getId()))
{
foundPreference = preference;
}
break;
}
}
if (foundPreference != null)
{
return foundPreference.getAttributes();
}
else
{
String errorMessage;
if (id == null)
{
errorMessage = String.format("Preference with name '%s' of type '%s' cannot be found",
name,
type);
}
else
{
errorMessage = String.format("Preference with name '%s' of type '%s' and id '%s' cannot be found",
name,
type,
id);
}
throw new NotFoundException(errorMessage);
}
}
else if (preferencesParts.size() == 1)
{
String type = preferencesParts.get(0);
List<Map<String, Object>> preferences = new ArrayList<>();
for (Preference preference : allPreferences)
{
if (preference.getType().equals(type))
{
if (id == null || id.equals(preference.getId()))
{
preferences.add(preference.getAttributes());
}
}
}
return preferences;
}
else if (preferencesParts.size() == 0)
{
final Map<String, List<Map<String, Object>>> preferences = new HashMap<>();
for (Preference preference : allPreferences)
{
if (id == null || id.equals(preference.getId()))
{
final String type = preference.getType();
if (!preferences.containsKey(type))
{
preferences.put(type, new ArrayList<Map<String, Object>>());
}
preferences.get(type).add(preference.getAttributes());
}
}
return preferences;
}
else
{
throw new IllegalArgumentException(String.format("unexpected path '%s'",
Joiner.on("/").join(preferencesParts)));
}
}
private <T> T awaitFuture(final ListenableFuture<T> future)
{
return FutureHelper.<T, RuntimeException>await(future, _preferenceOperationTimeout, TimeUnit.MILLISECONDS);
}
private List<Preference> validateAndConvert(final ConfiguredObject<?> target, final Map<String, Object> providedObjectMap)
{
List<Preference> replacementPreferences = new ArrayList<>();
for (String type : providedObjectMap.keySet())
{
if (!(providedObjectMap.get(type) instanceof List))
{
final String errorMessage = String.format("expected a list of objects for attribute '%s'", type);
throw new IllegalArgumentException(errorMessage);
}
for (Object preferenceObject : (List<Object>) providedObjectMap.get(type))
{
if (!(preferenceObject instanceof Map))
{
final String errorMessage =
String.format("encountered non preference object in list of type '%s'", type);
throw new IllegalArgumentException(errorMessage);
}
Map<String, Object> preferenceAttributes = (Map<String, Object>) preferenceObject;
ensureAttributeMatches(preferenceAttributes, "type", type);
Preference preference = PreferenceFactory.fromAttributes(target, preferenceAttributes);
replacementPreferences.add(preference);
}
}
return replacementPreferences;
}
private List<Preference> validateAndConvert(final ConfiguredObject<?> target,
final String type,
final List<Object> providedObjects)
{
List<Preference> replacementPreferences = new ArrayList<>();
for (Object preferenceObject : providedObjects)
{
if (!(preferenceObject instanceof Map))
{
throw new IllegalArgumentException("expected a list of objects");
}
Map<String, Object> preferenceAttributes = (Map<String, Object>) preferenceObject;
ensureAttributeMatches(preferenceAttributes, "type", type);
Preference preference = PreferenceFactory.fromAttributes(target, preferenceAttributes);
replacementPreferences.add(preference);
}
return replacementPreferences;
}
private UUID getIdFromQueryParameters(final Map<String, List<String>> queryParameters)
{
List<String> ids = queryParameters.get("id");
if (ids != null && ids.size() > 1)
{
throw new IllegalArgumentException("Multiple ids in query string are not allowed");
}
return (ids == null ? null : UUID.fromString(ids.get(0)));
}
private void ensureAttributeMatches(final Map<String, Object> preferenceAttributes,
final String attributeName,
final String expectedValue)
{
final Object providedValue = preferenceAttributes.get(attributeName);
if (providedValue != null)
{
if (!Objects.equals(providedValue, expectedValue))
{
final String errorMessage = String.format(
"The attribute '%s' within the payload ('%s') contradicts the value implied by the url ('%s')",
attributeName,
providedValue,
expectedValue);
throw new IllegalArgumentException(errorMessage);
}
}
else
{
preferenceAttributes.put(attributeName, expectedValue);
}
}
private String getProvidedAttributeAsString(final Map<String, Object> preferenceAttributes,
final String attributeName)
{
Object providedValue = preferenceAttributes.get(attributeName);
if (providedValue != null && !(providedValue instanceof String))
{
final String errorMessage = String.format("Attribute '%s' must be of type string. Found : '%s'",
attributeName, providedValue);
throw new IllegalArgumentException(errorMessage);
}
return (String) providedValue;
}
}