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

io.permazen.spring.PermazenObjectHttpMessageConverter Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.spring;

import com.google.common.base.Preconditions;

import io.permazen.DetachedPermazenTransaction;
import io.permazen.Permazen;
import io.permazen.PermazenObject;
import io.permazen.core.ObjId;

import jakarta.validation.groups.Default;

import java.io.IOException;
import java.util.Collections;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;

/**
 * Spring {@link org.springframework.http.converter.HttpMessageConverter HttpMessageConverter} capable of
 * encoding and decoding one or more {@link PermazenObject}s contained in a {@link DetachedPermazenTransaction}.
 *
 * 

* The payload MIME type is set to {@code application/x-permazen-transaction} with an additional {@code root} * parameter specifying the root {@link PermazenObject} in the encoded transaction. * There may of course be arbitrarily many other supporting {@link PermazenObject}s riding along with it. * *

* Validation of all incoming objects is supported; see {@link #setValidationGroups setValidationGroups()}. * * @see DetachedPermazenTransactionHttpMessageConverter * @see KVStoreHttpMessageConverter */ public class PermazenObjectHttpMessageConverter extends AbstractHttpMessageConverter { /** * Name of the object ID parameter incluced in the MIME type. */ public static final String ROOT_OBJECT_ID_PARAMETER_NAME = "root"; private final Permazen pdb; private Class[] validationGroups; /** * Constructor. * * @param pdb {@link Permazen} instance * @throws IllegalArgumentException if {@code pdb} is null */ public PermazenObjectHttpMessageConverter(Permazen pdb) { this(pdb, DetachedPermazenTransactionHttpMessageConverter.MIME_TYPE, DetachedPermazenTransactionHttpMessageConverter.LEGACY_MIME_TYPE); } /** * Constructor. * * @param pdb {@link Permazen} instance * @param supportedMediaTypes supported media types * @throws IllegalArgumentException if {@code pdb} is null */ public PermazenObjectHttpMessageConverter(Permazen pdb, MediaType... supportedMediaTypes) { super(supportedMediaTypes); Preconditions.checkArgument(pdb != null, "null pdb"); this.pdb = pdb; } /** * Set validation groups used to validate all incoming objects. * *

* If set to null, no validation is performed; this is the default behavior. * *

* Otherwise, validation of all incoming objects (not just the root object) is performed using the specified * validation groups, or {@link Default} if an empty array is specified. * * @param groups validation group(s) to use for validation; if empty, {@link Default} is assumed; * if null, no validation is performed */ public void setValidationGroups(Class... groups) { this.validationGroups = groups; } // AbstractHttpMessageConverter @Override protected Long getContentLength(PermazenObject jobj, MediaType contentType) { return KVStoreHttpMessageConverter.getKVStoreContentLength( jobj.getPermazenTransaction().getTransaction().getKVTransaction()); } @Override public boolean canRead(Class clazz, MediaType mediaType) { return super.canRead(mediaType); } @Override protected boolean supports(Class target) { return this.pdb.findPermazenClass(target) != null; } @Override protected MediaType getDefaultContentType(PermazenObject jobj) { Preconditions.checkArgument(jobj != null, "null jobj"); return new MediaType(this.getSupportedMediaTypes().get(0), Collections.singletonMap(ROOT_OBJECT_ID_PARAMETER_NAME, jobj.getObjId().toString())); } @Override protected PermazenObject readInternal(Class type, HttpInputMessage input) throws IOException { // Decode the detached transaction final DetachedPermazenTransaction jtx = DetachedPermazenTransactionHttpMessageConverter.readDetachedTransaction( this.pdb, input, this.validationGroups); // Get the root object's ID final MediaType mediaType = input.getHeaders().getContentType(); if (mediaType == null) { throw new HttpMessageNotReadableException(String.format( "required parameter \"%s\" missing; no \"%s\" header found", ROOT_OBJECT_ID_PARAMETER_NAME, HttpHeaders.CONTENT_TYPE), input); } final String objId = mediaType.getParameter(ROOT_OBJECT_ID_PARAMETER_NAME); if (objId == null) { throw new HttpMessageNotReadableException(String.format( "required parameter \"%s\" missing from %s \"%s\"", ROOT_OBJECT_ID_PARAMETER_NAME, HttpHeaders.CONTENT_TYPE, mediaType), input); } final ObjId id; try { id = new ObjId(objId); } catch (IllegalArgumentException e) { throw new HttpMessageNotReadableException(String.format( "invalid \"%s\" parameter value \"%s\" in %s \"%s\"", ROOT_OBJECT_ID_PARAMETER_NAME, objId, HttpHeaders.CONTENT_TYPE, mediaType), input); } // Find the root object final PermazenObject jobj = jtx.get(id); if (!jobj.exists()) { throw new HttpMessageNotReadableException(String.format( "no object with object ID %s found in object graph", id), input); } // Done return jobj; } @Override protected void writeInternal(PermazenObject jobj, HttpOutputMessage output) throws IOException { output.getHeaders().setContentType(this.getDefaultContentType(jobj)); KVStoreHttpMessageConverter.writeKVStore(jobj.getPermazenTransaction().getTransaction().getKVTransaction(), output); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy