ElasticJacksonAnnotationIntrospector.java

/*
 * Copyright 2020 Global Crop Diversity Trust
 *
 * 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 org.gringlobal.component.elastic;

import java.util.List;

import org.genesys.blocks.security.serialization.CurrentPermissionsWriter;
import org.gringlobal.custom.elasticsearch.IgnoreField;

import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.introspect.ObjectIdInfo;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;

/**
 * Extra controls to what gets serialized to JSON before sending data over to
 * ES.
 *
 * - Ignores @Field(index = no) properties
 * - Ignore @JsonIdentityInfo on classes to prevent them being serialized as ID-only
 * - Ignore CurrentPermissionsWriter ("_permissions")
 *
 * @author Matija Obreza
 */
public class ElasticJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {

	private static final long serialVersionUID = -6537804989972456483L;

	/**
	 * Allows us to ignore @JsonIdentityInfo on classes to prevent them being serialized as ID-only
	 */
	@Override
	public ObjectIdInfo findObjectIdInfo(Annotated ann) {
		if (ann instanceof AnnotatedClass) {
			// Ignore @JsonIdentityInfo on classes
			// System.err.println("Ignoring @JsonIdentityInfo for=" + ann);
			return null;
		} else {
			// AnnotatedMethods and Fields
			JsonIdentityReference idRef = _findAnnotation(ann, JsonIdentityReference.class);
			if (idRef == null || ! idRef.alwaysAsId()) {
				// System.err.println("@IdI of " + ann.getClass() + " " + ann);
				return null;
			}
		}
		return super.findObjectIdInfo(ann);
	}

	/**
	 * We don't want to serialize _permissions property to ES
	 */
	@Override
	public void findAndAddVirtualProperties(MapperConfig<?> config, AnnotatedClass ac, List<BeanPropertyWriter> properties) {
		super.findAndAddVirtualProperties(config, ac, properties);

		// Remove current permissions writer
		properties.removeIf(bpw -> bpw instanceof CurrentPermissionsWriter);
	}

	/**
	 * Ignore properties where @Fileld(index = no)
	 */
	@Override
	protected boolean _isIgnorable(Annotated a) {
		IgnoreField ignoreField = _findAnnotation(a, IgnoreField.class);
		if (ignoreField != null) {
			return ignoreField.value();
		}
		return super._isIgnorable(a);
	}

	@Override
	public Boolean hasRequiredMarker(AnnotatedMember m) {
		IgnoreField ignoreField = _findAnnotation(m, IgnoreField.class);
		if (ignoreField != null && ignoreField.value()) {
			return false;
		}
		return super.hasRequiredMarker(m);
	}
	
	private boolean isExcludedFromEs(Annotated a) {
		IgnoreField ignoreField = _findAnnotation(a, IgnoreField.class);
		if (ignoreField != null) {
			// System.err.println("Should not serialize @IgnoreField on " + a);
		}
		return ignoreField != null && ignoreField.value();
	}

	@Override
	public Class<?>[] findViews(Annotated a) {
		if (isExcludedFromEs(a)) {
			return new Class<?>[] { NotIndexed.class };
		}
		return super.findViews(a);
	}

	public static class NotIndexed {

	}
}