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