
org.microbean.kubernetes.controller.AbstractEvent Maven / Gradle / Ivy
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
*
* Copyright © 2017-2018 microBean.
*
* 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.microbean.kubernetes.controller;
import java.io.Serializable; // for javadoc only
import java.time.Duration;
import java.time.Instant;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Objects;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.ObjectMeta;
/**
* An {@code abstract} {@link EventObject} that represents another
* event that has occurred to a Kubernetes resource, usually as found
* in an {@link EventCache} implementation.
*
* @param a type of Kubernetes resource
*
* @author Laird Nelson
*
* @see EventCache
*/
public abstract class AbstractEvent extends EventObject {
/*
* Static fields.
*/
/**
* The version of this class for {@linkplain Serializable
* serialization purposes}.
*
* @see Serializable
*/
private static final long serialVersionUID = 1L;
/*
* Instance fields.
*/
/**
* The key that identifies this {@link AbstractEvent}'s {@linkplain
* #getResource() resource} only when its final state is
* unknown.
*
* This field can be—and often is—{@code null}.
*
* @see #getKey()
*
* @see #setKey(Object)
*/
private volatile Object key;
/**
* The {@link Type} describing the type of this {@link
* AbstractEvent}.
*
* This field is never {@code null}.
*
* @see #getType()
*/
private final Type type;
/**
* A Kubernetes resource representing the prior state of
* the resource returned by this {@link AbstractEvent}'s {@link
* #getResource()} method.
*
* This field may be {@code null}.
*
* The prior state of a given Kubernetes resource is often not
* known, so this field is often {@code null}.
*
* @see #getResource()
*/
private final T priorResource;
/**
* A Kubernetes resource representing its state at the time of this
* event.
*
* This field is never {@code null}.
*
* @see #getResource()
*/
private final T resource;
/*
* Constructors.
*/
/**
* A private zero-argument constructor to reinforce to readers and
* subclassers alike that this is not only an {@code abstract}
* class, but one with a finite, known number of subclasses.
*
* @exception NullPointerException when invoked
*
* @see #AbstractEvent(Object, Type, HasMetadata, HasMetadata)
*/
private AbstractEvent() {
this(null, null, null, null);
}
/**
* Creates a new {@link AbstractEvent}.
*
* @param source the creator; must not be {@code null}
*
* @param type the {@link Type} of this {@link AbstractEvent}; must not be
* {@code null}
*
* @param priorResource a {@link HasMetadata} representing the
* prior state of the {@linkplain #getResource() Kubernetes
* resource this AbstractEvent
primarily concerns}; may
* be—and often is—null
*
* @param resource a {@link HasMetadata} representing a Kubernetes
* resource; must not be {@code null}
*
* @exception NullPointerException if {@code source}, {@code type}
* or {@code resource} is {@code null}
*
* @exception IllegalStateException if somehow a subclass invoking
* this constructor manages illicitly to be neither an instance of
* {@link Event} nor an instance of {@link SynchronizationEvent}
*
* @see Type
*
* @see EventObject#getSource()
*/
AbstractEvent(final Object source, final Type type, final T priorResource, final T resource) {
super(source);
if (!(Event.class.isAssignableFrom(this.getClass()) || SynchronizationEvent.class.isAssignableFrom(this.getClass()))) {
throw new IllegalStateException("Unexpected subclass");
}
this.type = Objects.requireNonNull(type);
this.priorResource = priorResource;
this.resource = Objects.requireNonNull(resource);
}
/*
* Instance methods.
*/
/**
* Returns a {@link Type} representing the type of this {@link
* AbstractEvent}.
*
* This method never returns {@code null}.
*
* @return a non-{@code null} {@link Type}
*
* @see Type
*/
public final Type getType() {
return this.type;
}
/**
* Returns a {@link HasMetadata} representing the prior
* state of the Kubernetes resource this {@link AbstractEvent}
* primarily concerns.
*
* This method may return {@code null}, and often does.
*
* The prior state of a Kubernetes resource is often not known at
* {@link AbstractEvent} construction time so it is common for this method
* to return {@code null}.
*
* @return a {@link HasMetadata} representing the prior
* state of the {@linkplain #getResource() Kubernetes resource
* this AbstractEvent
primarily concerns}, or {@code null}
*
* @see #getResource()
*/
public final T getPriorResource() {
return this.priorResource;
}
/**
* Returns a {@link HasMetadata} representing the Kubernetes
* resource this {@link AbstractEvent} concerns.
*
*
This method never returns {@code null}.
*
* @return a non-{@code null} Kubernetes resource
*/
public final T getResource() {
return this.resource;
}
/**
* Returns {@code true} if this {@link AbstractEvent}'s {@linkplain
* #getResource() resource} is an accurate representation of its
* last known state.
*
* This should only return {@code true} for some, but not all,
* deletion scenarios. Any other behavior should be considered to
* be an error.
*
* @return {@code true} if this {@link AbstractEvent}'s {@linkplain
* #getResource() resource} is an accurate representation of its
* last known state; {@code false} otherwise
*/
public final boolean isFinalStateKnown() {
return this.key == null;
}
/**
* Sets the key identifying the Kubernetes resource this {@link
* AbstractEvent} describes.
*
* @param key the new key; may be {@code null}
*
* @see #getKey()
*/
final void setKey(final Object key) {
this.key = key;
}
/**
* Returns a key that can be used to unambiguously identify this
* {@link AbstractEvent}'s {@linkplain #getResource() resource}.
*
* This method may return {@code null} in exceptional cases, but
* normally does not.
*
* Overrides of this method must not return {@code null} except
* in exceptional cases.
*
* The default implementation of this method returns the return
* value of the {@link HasMetadatas#getKey(HasMetadata)} method.
*
* @return a key for this {@link AbstractEvent}, or {@code null}
*
* @see HasMetadatas#getKey(HasMetadata)
*/
public Object getKey() {
Object returnValue = this.key;
if (returnValue == null) {
returnValue = HasMetadatas.getKey(this.getResource());
}
return returnValue;
}
/**
* Returns a hashcode for this {@link AbstractEvent}.
*
* @return a hashcode for this {@link AbstractEvent}
*/
@Override
public int hashCode() {
int hashCode = 37;
final Object source = this.getSource();
int c = source == null ? 0 : source.hashCode();
hashCode = hashCode * 17 + c;
final Object key = this.getKey();
c = key == null ? 0 : key.hashCode();
hashCode = hashCode * 17 + c;
final Object type = this.getType();
c = type == null ? 0 : type.hashCode();
hashCode = hashCode * 17 + c;
final Object resource = this.getResource();
c = resource == null ? 0 : resource.hashCode();
hashCode = hashCode * 17 + c;
final Object priorResource = this.getPriorResource();
c = priorResource == null ? 0 : priorResource.hashCode();
hashCode = hashCode * 17 + c;
return hashCode;
}
/**
* Returns {@code true} if the supplied {@link Object} is also an
* {@link AbstractEvent} and is equal in every respect to this one.
*
* @param other the {@link Object} to test; may be {@code null} in
* which case {@code false} will be returned
*
* @return {@code true} if the supplied {@link Object} is also an
* {@link AbstractEvent} and is equal in every respect to this one; {@code
* false} otherwise
*/
@Override
public boolean equals(final Object other) {
if (other == this) {
return true;
} else if (other instanceof AbstractEvent) {
final AbstractEvent> her = (AbstractEvent>)other;
final Object source = this.getSource();
if (source == null) {
if (her.getSource() != null) {
return false;
}
} else if (!source.equals(her.getSource())) {
return false;
}
final Object key = this.getKey();
if (key == null) {
if (her.getKey() != null) {
return false;
}
} else if (!key.equals(her.getKey())) {
return false;
}
final Object type = this.getType();
if (type == null) {
if (her.getType() != null) {
return false;
}
} else if (!type.equals(her.getType())) {
return false;
}
final Object resource = this.getResource();
if (resource == null) {
if (her.getResource() != null) {
return false;
}
} else if (!resource.equals(her.getResource())) {
return false;
}
final Object priorResource = this.getPriorResource();
if (priorResource == null) {
if (her.getPriorResource() != null) {
return false;
}
} else if (!priorResource.equals(her.getPriorResource())) {
return false;
}
return true;
} else {
return false;
}
}
/**
* Returns a {@link String} representation of this {@link AbstractEvent}.
*
* This method never returns {@code null}.
*
* Overrides of this method must not return {@code null}.
*
* @return a non-{@code null} {@link String} representation of this
* {@link AbstractEvent}
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder().append(this.getType()).append(": ");
final Object priorResource = this.getPriorResource();
if (priorResource != null) {
sb.append(priorResource).append(" --> ");
}
sb.append(this.getResource());
return sb.toString();
}
/*
* Inner and nested classes.
*/
/**
* The type of an {@link AbstractEvent}.
*
* @author Laird Nelson
*/
public static enum Type {
/**
* A {@link Type} representing the addition of a resource.
*/
ADDITION,
/**
* A {@link Type} representing the modification of a resource.
*/
MODIFICATION,
/**
* A {@link Type} representing the deletion of a resource.
*/
DELETION
}
}