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