001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2023 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.scopelet; 015 016import java.lang.invoke.MethodHandles; 017import java.lang.invoke.MethodHandles.Lookup; 018import java.lang.invoke.VarHandle; 019 020import java.util.Objects; 021 022import org.microbean.bean.Bean; 023import org.microbean.bean.Creation; 024import org.microbean.bean.Id; 025import org.microbean.bean.Factory; 026import org.microbean.bean.ReferenceSelector; 027 028import org.microbean.qualifier.NamedAttributeMap; 029 030import org.microbean.scope.ScopeMember; 031 032public abstract class Scopelet<S extends Scopelet<S>> implements AutoCloseable, Factory<S>, ScopeMember { 033 034 private static final VarHandle ME; 035 036 static { 037 final Lookup lookup = MethodHandles.lookup(); 038 try { 039 ME = lookup.findVarHandle(Scopelet.class, "me", Scopelet.class); 040 } catch (final NoSuchFieldException | IllegalAccessException e) { 041 throw (Error)new ExceptionInInitializerError(e.getMessage()).initCause(e); 042 } 043 } 044 045 private volatile S me; 046 047 private volatile boolean closed; 048 049 private final NamedAttributeMap<?> scopeId; 050 051 protected Scopelet(final NamedAttributeMap<?> scopeId) { 052 super(); 053 this.scopeId = Objects.requireNonNull(scopeId, "scopeId"); 054 } 055 056 public abstract Id id(); 057 058 public final Bean<S> bean() { 059 return new Bean<>(this.id(), this); 060 } 061 062 @Override // Factory<S> 063 @SuppressWarnings("unchecked") 064 public final S create(final Creation<S> c, final ReferenceSelector referenceSelector) { 065 if (ME.compareAndSet(this, null, this)) { // volatile write 066 if (referenceSelector != null) { 067 // TODO: emit initialized event 068 } 069 } 070 return (S)this; 071 } 072 073 @Override // Factory<S> 074 public final S singleton() { 075 return this.me; // volatile read 076 } 077 078 @Override // Factory<S> 079 public boolean destroys() { 080 return true; 081 } 082 083 @Override // Object 084 public int hashCode() { 085 int hashCode = 17; 086 hashCode = 31 * hashCode + this.id().hashCode(); 087 hashCode = 31 * hashCode + this.scopeId().hashCode(); 088 return hashCode; 089 } 090 091 @Override // Object 092 public boolean equals(final Object other) { 093 if (other == this) { 094 return true; 095 } else if (other != null && other.getClass().equals(this.getClass())) { 096 final Scopelet<?> her = (Scopelet<?>)other; 097 return 098 Objects.equals(this.id(), her.id()) && 099 Objects.equals(this.scopeId(), her.scopeId()); 100 } else { 101 return false; 102 } 103 } 104 105 @Override // ScopeMember 106 public final NamedAttributeMap<?> governingScopeId() { 107 return this.id().governingScopeId(); 108 } 109 110 @Override // ScopeMember 111 public final boolean governedBy(final NamedAttributeMap<?> scopeId) { 112 return this.id().governedBy(scopeId); 113 } 114 115 116 /* 117 * Repository-like concerns. 118 */ 119 120 121 public final NamedAttributeMap<?> scopeId() { 122 return this.scopeId; 123 } 124 125 public boolean active() { 126 return !this.closed(); // volatile read 127 } 128 129 public boolean containsId(final Object id) { 130 if (!this.active()) { 131 throw new InactiveScopeletException(); 132 } 133 return this.get(id) != null; 134 } 135 136 // id is nullable. 137 public <I> I get(final Object id) { 138 if (!this.active()) { 139 throw new InactiveScopeletException(); 140 } 141 return this.instance(id, null, null, null); 142 } 143 144 // All parameters are nullable. 145 public abstract <I> I instance(final Object id, 146 final Factory<I> factory, 147 final Creation<I> c, 148 final ReferenceSelector r); 149 150 // id is nullable. 151 public abstract boolean remove(final Object id); 152 153 @Override // AutoCloseable 154 public void close() { 155 if (!this.closed()) { 156 this.closed = true; 157 } 158 } 159 160 protected final boolean closed() { 161 return this.closed; 162 } 163 164}