All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.http.webdav.PropertySourcePatchSetter Maven / Gradle / Ivy

Go to download

Milton Community Edition: Supports DAV level 1 and is available on Apache2 license

There is a newer version: 4.0.3.2215
Show newest version
/*
 * 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 io.milton.http.webdav;

import io.milton.http.Response;
import io.milton.http.Response.Status;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.values.ValueAndType;
import io.milton.http.values.ValueWriters;
import io.milton.http.webdav.PropFindResponse.NameAndError;
import io.milton.property.PropertySource;
import io.milton.property.PropertySource.PropertyMetaData;
import io.milton.property.PropertySource.PropertySetException;
import io.milton.resource.Resource;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.namespace.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author brad
 */
public class PropertySourcePatchSetter implements PropPatchSetter {

	private static final Logger log = LoggerFactory.getLogger(PropertySourcePatchSetter.class);
	private final List propertySources;
	private final ValueWriters valueWriters;

	public PropertySourcePatchSetter(List propertySources, ValueWriters valueWriters) {
		this.propertySources = propertySources;
		this.valueWriters = valueWriters;
	}

	public PropertySourcePatchSetter(List propertySources) {
		this.propertySources = propertySources;
		this.valueWriters = new ValueWriters();
	}

	/**
	 * This returns true for all resources, but it actually depends on the
	 * configured property sources.
	 *
	 * If no property sources support a given resource, a proppatch attempt
	 * will return 404's for all properties
	 *
	 * @param r
	 * @return
	 */
	@Override
	public boolean supports(Resource r) {
		return true;
	}

	@Override
	public PropFindResponse setProperties(String href, PropPatchParseResult parseResult, Resource r) throws NotAuthorizedException, BadRequestException {
		log.trace("setProperties: resource type: {}", r.getClass());
		Map knownProps = new HashMap<>();

		Map> errorProps = new EnumMap<>(Status.class);
		for (Entry entry : parseResult.getFieldsToSet().entrySet()) {
			QName name = entry.getKey();
			boolean found = false;
			for (PropertySource source : propertySources) {
				PropertyMetaData meta = source.getPropertyMetaData(entry.getKey(), r);
				if (meta != null && meta.isNotUnknownOrAlwaysWriteable()) {
					found = true;
					if (meta.isAlwaysWriteable()) {
						Object val = parse(name, entry.getValue(), meta.getValueType());
						try {
							log.trace("setProperties: name: {} source: {}", name, source.getClass());
							source.setProperty(name, val, r);
							knownProps.put(name, new ValueAndType(null, meta.getValueType()));
							break;
						} catch (NotAuthorizedException e) {
							log.warn("setProperties: NotAuthorised to write property: {}", name, e);
							addErrorProp(errorProps, Response.Status.SC_UNAUTHORIZED, name, "Not authorised");
							break;
						} catch (PropertySetException ex) {
							log.warn("setProperties: PropertySetException when writing property {}", name, ex);
							addErrorProp(errorProps, ex.getStatus(), name, ex.getErrorNotes());
							break;
						}
					} else {
						log.warn("property is not writable in source: " + source.getClass());
						addErrorProp(errorProps, Response.Status.SC_FORBIDDEN, name, "Property is read only");
						break;
					}
				} else {
					//log.debug( "not found in: " + source.getClass().getCanonicalName() );
				}
			}
			if (!found) {
				log.warn("property not found: " + entry.getKey() + " on resource: " + r.getClass());
				addErrorProp(errorProps, Status.SC_NOT_FOUND, entry.getKey(), "Unknown property");
			}
		}
		if (parseResult.getFieldsToRemove() != null) {
			for (QName name : parseResult.getFieldsToRemove()) {
				boolean found = false;
				for (PropertySource source : propertySources) {
					PropertyMetaData meta = source.getPropertyMetaData(name, r);
					if (meta != null && !meta.isUnknown()) {
						found = true;
						if (meta.isWritable()) {
							try {
								log.trace("clearProperty");
								source.clearProperty(name, r);
								knownProps.put(name, new ValueAndType(null, meta.getValueType()));
								break;
							} catch (NotAuthorizedException e) {
								addErrorProp(errorProps, Response.Status.SC_UNAUTHORIZED, name, "Not authorised");
								break;
							} catch (PropertySetException ex) {
								addErrorProp(errorProps, ex.getStatus(), name, ex.getErrorNotes());
								break;
							}
						} else {
							log.warn("property is not writable in source: " + source.getClass());
							addErrorProp(errorProps, Response.Status.SC_FORBIDDEN, name, "Property is read only");
							break;
						}
					} else {
						//log.debug( "not found in: " + source.getClass().getCanonicalName() );
					}
				}
				if (!found) {
					log.warn("property not found to remove: " + name);
					addErrorProp(errorProps, Status.SC_NOT_FOUND, name, "Unknown property");
				}
			}
		}
		if (log.isDebugEnabled()) {
			if (errorProps.size() > 0) {
				log.debug("errorProps: " + errorProps.size() + " listing property sources:");
				for (PropertySource s : propertySources) {
					log.debug("  source: " + s.getClass().getCanonicalName());
				}
			}
		}
		if( r instanceof CommitableResource) {
			log.trace("resource is commitable, call doCommit");
			CommitableResource cr = (CommitableResource) r;
			cr.doCommit(knownProps, errorProps);
		} else {
			log.trace("resource is not commitable");
		}
		return new PropFindResponse(href, knownProps, errorProps);
	}

	private void addErrorProp(Map> errorProps, Status stat, QName name, String err) {
		List list = errorProps.computeIfAbsent(stat, k -> new ArrayList<>());
		NameAndError ne = new NameAndError(name, err);
		list.add(ne);

	}

	private Object parse(QName key, String value, Class valueType) {
		return valueWriters.parse(key, valueType, value);
	}

	public interface CommitableResource extends Resource {

		void doCommit(Map knownProps, Map> errorProps) throws BadRequestException, NotAuthorizedException ;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy