001/* 002 * JGrapes Event Driven Framework 003 * Copyright (C) 2016-2026 Michael N. Lipp 004 * 005 * This program is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU Affero General Public License as published by 007 * the Free Software Foundation; either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, but 011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public 013 * License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program; if not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jgrapes.core.internal; 020 021import java.lang.annotation.Annotation; 022import java.lang.reflect.Method; 023import java.util.ArrayList; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.ConcurrentModificationException; 027import java.util.Iterator; 028import java.util.List; 029import java.util.NoSuchElementException; 030import java.util.Stack; 031import java.util.concurrent.ExecutorService; 032import org.jgrapes.core.Channel; 033import org.jgrapes.core.ComponentType; 034import org.jgrapes.core.Components; 035import org.jgrapes.core.Event; 036import org.jgrapes.core.EventPipeline; 037import org.jgrapes.core.HandlerScope; 038import org.jgrapes.core.Manager; 039import org.jgrapes.core.annotation.HandlerDefinition; 040import org.jgrapes.core.annotation.HandlerDefinition.ChannelReplacements; 041import org.jgrapes.core.events.Attached; 042import org.jgrapes.core.events.Detached; 043import org.jgrapes.core.events.Start; 044 045/** 046 * ComponentVertex is the base class for all nodes in the component tree. 047 * ComponentVertex is extended by {@link org.jgrapes.core.Component} 048 * for the use as base class for component implementations. As an 049 * alternative for implementing components with an independent base class, 050 * the derived class {@link org.jgrapes.core.internal.ComponentProxy} can be 051 * used. 052 */ 053@SuppressWarnings({ "PMD.TooManyMethods", "PMD.GodClass", 054 "PMD.CouplingBetweenObjects", "PMD.AvoidSynchronizedStatement" }) 055public abstract class ComponentVertex implements Manager, Channel { 056 057 /** The component's (optional) name. */ 058 private String name; 059 /** Reference to the common properties of the tree nodes. */ 060 private ComponentTree tree; 061 /** Reference to the parent node. */ 062 private ComponentVertex parent; 063 /** All the node's children */ 064 private final List<ComponentVertex> children = new ArrayList<>(); 065 /** The handlers provided by this component. */ 066 private List<HandlerReference> handlers; 067 /** The channel replacements. */ 068 @SuppressWarnings({ "PMD.LooseCoupling", "PMD.AvoidDuplicateLiterals" }) 069 private final ChannelReplacements channelReplacements; 070 071 /** 072 * 073 * Initialize the ComponentVertex. By default it forms a stand-alone 074 * tree, i.e. the root is set to the component itself. 075 * 076 * @param channelReplacements the channel replacements 077 */ 078 @SuppressWarnings("PMD.LooseCoupling") 079 protected ComponentVertex(ChannelReplacements channelReplacements) { 080 this.channelReplacements = channelReplacements; 081 } 082 083 /** 084 * Return the channel replacements passed to the constructor. 085 * 086 * @return the channel replacements 087 */ 088 @SuppressWarnings("PMD.LooseCoupling") 089 public ChannelReplacements channelReplacements() { 090 return channelReplacements; 091 } 092 093 /** 094 * Initialize the handler list of this component. May only be called 095 * when {@link #component()} can be relied on to return the 096 * correct value. 097 */ 098 protected void initComponentsHandlers() { 099 handlers = new ArrayList<>(); 100 // Have a look at all methods. 101 for (Method m : component().getClass().getMethods()) { 102 maybeAddHandler(m, channelReplacements); 103 } 104 handlers = Collections.synchronizedList(handlers); 105 } 106 107 @SuppressWarnings("PMD.LooseCoupling") 108 private void maybeAddHandler( 109 Method method, ChannelReplacements channelReplacements) { 110 for (Annotation annotation : method.getDeclaredAnnotations()) { 111 Class<?> annoType = annotation.annotationType(); 112 HandlerDefinition hda = annoType.getAnnotation( 113 HandlerDefinition.class); 114 if (hda == null) { 115 continue; 116 } 117 HandlerDefinition.Evaluator evaluator 118 = CoreUtils.definitionEvaluator(hda); 119 HandlerScope scope = evaluator.scope(component(), method, 120 channelReplacements); 121 if (scope == null) { 122 continue; 123 } 124 handlers.add(HandlerReference.newRef( 125 component(), method, evaluator.priority(annotation), 126 scope)); 127 } 128 } 129 130 /* 131 * (non-Javadoc) 132 * 133 * @see org.jgrapes.core.Manager#setName(java.lang.String) 134 */ 135 @Override 136 public ComponentType setName(String name) { 137 this.name = name; 138 return component(); 139 } 140 141 /* 142 * (non-Javadoc) 143 * 144 * @see org.jgrapes.core.Manager#name() 145 */ 146 @Override 147 public String name() { 148 return name; 149 } 150 151 /* 152 * (non-Javadoc) 153 * 154 * @see org.jgrapes.core.Manager#path() 155 */ 156 @Override 157 public String componentPath() { 158 StringBuilder res = new StringBuilder(); 159 buildPath(res); 160 return res.toString(); 161 } 162 163 private void buildPath(StringBuilder builder) { 164 if (parent != null) { 165 parent.buildPath(builder); 166 } 167 builder.append('/') 168 .append(name == null ? getClass().getSimpleName() : name); 169 } 170 171 /** 172 * Return the component node for a given component. 173 * 174 * @param component the component 175 * @param componentChannel the component's channel 176 * @return the node representing the component in the tree 177 */ 178 public static ComponentVertex componentVertex( 179 ComponentType component, Channel componentChannel) { 180 if (component instanceof ComponentVertex) { 181 return (ComponentVertex) component; 182 } 183 return ComponentProxy.getComponentProxy(component, componentChannel); 184 } 185 186 /** 187 * Returns the component represented by this node in the tree. 188 * 189 * @return the component 190 */ 191 @Override 192 public abstract ComponentType component(); 193 194 /* 195 * (non-Javadoc) 196 * 197 * @see org.jgrapes.core.Manager#getChildren() 198 */ 199 @Override 200 public List<ComponentType> children() { 201 synchronized (this) { 202 List<ComponentType> children = new ArrayList<>(); 203 for (ComponentVertex child : this.children) { 204 children.add(child.component()); 205 } 206 return Collections.unmodifiableList(children); 207 } 208 } 209 210 /* 211 * (non-Javadoc) 212 * 213 * @see org.jgrapes.core.Manager#getParent() 214 */ 215 @Override 216 public ComponentType parent() { 217 synchronized (this) { 218 if (parent == null) { 219 return null; 220 } 221 return parent.component(); 222 } 223 } 224 225 /* 226 * (non-Javadoc) 227 * 228 * @see org.jgrapes.core.Manager#getRoot() 229 */ 230 @Override 231 public ComponentType root() { 232 return tree().root().component(); 233 } 234 235 /** 236 * Return the tree that this node belongs to. If the node does not 237 * belong to a tree yet, a tree is automatically created. 238 * 239 * @return the tree 240 */ 241 /* default */ ComponentTree tree() { 242 if (tree != null) { 243 return tree; 244 } 245 // Build complete tree before assigning it. 246 ComponentTree newTree = new ComponentTree(this); 247 newTree.setEventPipeline(new BufferingEventPipeline(newTree)); 248 tree = newTree; 249 fire(new Attached(component(), null), channel()); 250 return tree; 251 } 252 253 /** 254 * Set the reference to the common properties of this component 255 * and all its children to the given value. 256 * 257 * @param comp the new root 258 */ 259 private void setTree(ComponentTree tree) { 260 synchronized (this) { 261 this.tree = tree; 262 for (ComponentVertex child : children) { 263 child.setTree(tree); 264 } 265 } 266 } 267 268 /* 269 * (non-Javadoc) 270 * 271 * @see org.jgrapes.core.Manager#attach(Component) 272 */ 273 @Override 274 @SuppressWarnings({ "PMD.CyclomaticComplexity", "PMD.NcssCount", 275 "PMD.CognitiveComplexity" }) 276 public <T extends ComponentType> T attach(T child) { 277 synchronized (this) { 278 ComponentVertex childNode = componentVertex(child, null); 279 List<Channel> attachedAsChannels = new ArrayList<>(); 280 synchronized (childNode) { 281 if (tree != null && tree.isStarted()) { 282 for (TreeIterator itr = new TreeIterator(childNode); 283 itr.hasNext();) { 284 attachedAsChannels.add(itr.next()); 285 } 286 } 287 synchronized (tree()) { 288 if (childNode.tree == null) { 289 // Newly created, stand-alone child node 290 childNode.parent = this; 291 childNode.setTree(tree); 292 children.add(childNode); 293 } else { 294 // Attaching a tree... 295 synchronized (childNode.tree) { 296 if (childNode.parent != null) { 297 throw new IllegalStateException( 298 "Cannot attach a node with a parent."); 299 } 300 if (childNode.tree.isStarted()) { 301 throw new IllegalStateException( 302 "Cannot attach a started subtree."); 303 } 304 childNode.parent = this; 305 ComponentTree childTree = childNode.tree; 306 childNode.setTree(tree); 307 children.add(childNode); 308 tree.mergeEvents(childTree); 309 } 310 } 311 tree.clearHandlerCache(); 312 } 313 } 314 Channel parentChan = channel(); 315 if (parentChan == null) { 316 parentChan = BROADCAST; 317 } 318 Channel childChan = childNode.channel(); 319 if (childChan == null) { 320 parentChan = BROADCAST; 321 } 322 Attached evt = new Attached(childNode.component(), component()); 323 if (parentChan.equals(BROADCAST) || childChan.equals(BROADCAST)) { 324 fire(evt, BROADCAST); 325 } else if (parentChan.equals(childChan)) { 326 fire(evt, parentChan); 327 } else { 328 fire(evt, parentChan, childChan); 329 } 330 if (!attachedAsChannels.isEmpty()) { 331 fire(new Start(), attachedAsChannels.toArray(new Channel[0])); 332 } 333 return child; 334 } 335 } 336 337 /* 338 * (non-Javadoc) 339 * 340 * @see org.jgrapes.core.Manager#detach() 341 */ 342 @Override 343 public ComponentType detach() { 344 synchronized (this) { 345 if (parent != null) { 346 ComponentVertex oldParent = parent; 347 synchronized (tree) { 348 if (!tree.isStarted()) { 349 throw new IllegalStateException( 350 "Components may not be detached from a tree before" 351 + " a Start event has been fired on it."); 352 } 353 synchronized (oldParent) { 354 parent.children.remove(this); 355 parent.tree.clearHandlerCache(); 356 parent = null; 357 } 358 ComponentTree newTree = new ComponentTree(this); 359 newTree.setEventPipeline(new FeedBackPipelineFilter( 360 newTree, new EventProcessor(newTree))); 361 setTree(newTree); 362 } 363 Detached evt = new Detached(component(), oldParent.component()); 364 oldParent.fire(evt); 365 evt = new Detached(component(), oldParent.component()); 366 fire(evt); 367 } 368 return component(); 369 } 370 } 371 372 /* 373 * (non-Javadoc) 374 * 375 * @see java.lang.Iterable#iterator() 376 */ 377 @Override 378 public Iterator<ComponentType> iterator() { 379 return new ComponentIterator(new TreeIterator(this)); 380 } 381 382 /** 383 * A simple wrapper that converts a component vertex iterator 384 * to a component (type) iterator. 385 */ 386 private static class ComponentIterator implements Iterator<ComponentType> { 387 388 private final TreeIterator baseIterator; 389 390 /** 391 * @param baseIterator 392 */ 393 public ComponentIterator(TreeIterator baseIterator) { 394 this.baseIterator = baseIterator; 395 } 396 397 @Override 398 public boolean hasNext() { 399 return baseIterator.hasNext(); 400 } 401 402 @Override 403 public ComponentType next() { 404 return baseIterator.next().component(); 405 } 406 407 } 408 409 /** 410 * An iterator for getting all nodes of the tree. 411 */ 412 private static class TreeIterator implements Iterator<ComponentVertex> { 413 414 @SuppressWarnings({ "PMD.LooseCoupling" }) 415 private final Stack<CurPos> stack = new Stack<>(); 416 private final ComponentTree tree; 417 418 /** 419 * The current position. 420 */ 421 private class CurPos { 422 public final ComponentVertex current; 423 public final Iterator<ComponentVertex> childIter; 424 425 /** 426 * Instantiates a new current position 427 * 428 * @param vertex the cm 429 */ 430 public CurPos(ComponentVertex vertex) { 431 current = vertex; 432 childIter = current.children.iterator(); 433 } 434 } 435 436 /** 437 * Instantiates a new tree iterator. 438 * 439 * @param root the root 440 */ 441 public TreeIterator(ComponentVertex root) { 442 tree = root.tree(); 443 stack.push(new CurPos(root)); 444 } 445 446 /* 447 * (non-Javadoc) 448 * 449 * @see java.util.Iterator#hasNext() 450 */ 451 @Override 452 public boolean hasNext() { 453 return !stack.empty(); 454 } 455 456 /* 457 * (non-Javadoc) 458 * 459 * @see java.util.Iterator#next() 460 */ 461 @Override 462 public ComponentVertex next() { 463 if (stack.empty()) { 464 throw new NoSuchElementException(); 465 } 466 CurPos pos = stack.peek(); 467 ComponentVertex res = pos.current; 468 while (true) { 469 synchronized (pos.current) { 470 if (pos.current.tree != tree) { // NOPMD (comparison) 471 throw new ConcurrentModificationException(); 472 } 473 if (pos.childIter.hasNext()) { 474 stack.push(new CurPos(pos.childIter.next())); 475 break; 476 } 477 } 478 stack.pop(); 479 if (stack.empty()) { 480 break; 481 } 482 pos = stack.peek(); 483 } 484 return res; 485 } 486 487 /* 488 * (non-Javadoc) 489 * 490 * @see java.util.Iterator#remove() 491 */ 492 @Override 493 public void remove() { 494 throw new UnsupportedOperationException(); 495 } 496 } 497 498 @Override 499 public void addHandler(Method method, HandlerScope scope, int priority) { 500 handlers.add(HandlerReference.newRef(component(), 501 method, priority, scope)); 502 } 503 504 /* 505 * (non-Javadoc) 506 * 507 * @see org.jgrapes.core.core.Manager#fire 508 * (org.jgrapes.core.Event, org.jgrapes.core.Channel) 509 */ 510 @Override 511 public <T> Event<T> fire(Event<T> event, Channel... channels) { 512 if (channels.length == 0) { 513 channels = event.channels(); 514 if (channels.length == 0) { 515 channels = new Channel[] { channel() }; 516 } 517 } 518 event.setChannels(channels); 519 tree().fire(event, channels); 520 return event; 521 } 522 523 /** 524 * Collects all handlers. Iterates over the tree with this object 525 * as root and for all child components adds the matching handlers to 526 * the result set recursively. 527 * 528 * @param hdlrs the result set 529 * @param event the event to match 530 * @param channels the channels to match 531 */ 532 @SuppressWarnings("PMD.UseVarargs") 533 /* default */ void collectHandlers(Collection<HandlerReference> hdlrs, 534 EventBase<?> event, Channel[] channels) { 535 for (HandlerReference hdlr : handlers) { 536 if (hdlr.handles(event, channels)) { 537 hdlrs.add(hdlr); 538 } 539 } 540 for (ComponentVertex child : children) { 541 child.collectHandlers(hdlrs, event, channels); 542 } 543 } 544 545 /* 546 * (non-Javadoc) 547 * 548 * @see org.jgrapes.core.Manager#activeEventPipeline() 549 */ 550 @Override 551 public EventPipeline activeEventPipeline() { 552 return new CheckingPipelineFilter(tree(), 553 tree().eventPipeline(), channel()); 554 } 555 556 /* 557 * (non-Javadoc) 558 * 559 * @see org.jgrapes.core.Manager#newEventPipeline() 560 */ 561 @Override 562 public EventPipeline newEventPipeline() { 563 return new CheckingPipelineFilter(tree(), new EventProcessor(tree()), 564 channel()); 565 } 566 567 /* 568 * (non-Javadoc) 569 * 570 * @see org.jgrapes.core.Manager#newEventPipeline(java.util.concurrent. 571 * ExecutorService) 572 */ 573 @Override 574 public EventPipeline newEventPipeline(ExecutorService executorService) { 575 return new CheckingPipelineFilter(tree(), 576 new EventProcessor(tree(), executorService), channel()); 577 } 578 579 /** 580 * If a name has been set for this component 581 * (see {@link Manager#setName(String)}), return the name, 582 * else return the object name provided by 583 * {@link Components#objectName(Object)}, using 584 * {@link #component()} as argument. 585 */ 586 @Override 587 public String toString() { 588 if (name != null) { 589 return name; 590 } 591 return Components.objectName(component()); 592 } 593 594 /* 595 * (non-Javadoc) 596 * 597 * @see org.jgrapes.core.Manager#registerAsGenerator() 598 */ 599 @Override 600 public void registerAsGenerator() { 601 GeneratorRegistry.instance().add(component()); 602 } 603 604 /* 605 * (non-Javadoc) 606 * 607 * @see org.jgrapes.core.Manager#unregisterAsGenerator() 608 */ 609 @Override 610 public void unregisterAsGenerator() { 611 GeneratorRegistry.instance().remove(component()); 612 } 613 614}