/** * Inform the registered listeners about changes to the class node structure. * * Note that this method is synchronized. As it is ensured that only one thread can actively * change the structure this synchronization does not really hinder us. But is provides an * additional level of safety as the indexers can assume that they are only called single * threaded for sure. * * @param e * the changes to the structure. */ void informNodeChange(NodeEvent e) { for (INodeChangeListener listener : nodeChangeListeners) { listener.informNodeChange(e); } }
/** * Inform the registered listeners about changes to the class reference structure. * * Note that this method is synchronized. As it is ensured that only one thread can actively * change the structure this synchronization does not really hinder us. But is provides an * additional level of safety as the indexers can assume that they are only called single * threaded for sure. * * @param e * the changes to the structure. */ void informReferenceChange(ReferenceEvent e) { for (INodeChangeListener listener : nodeChangeListeners) { listener.informReferenceChange(e); } }
/** * {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((eventDetails == null) ? 0 : eventDetails.hashCode()); result = (prime * result) + ((eventType == null) ? 0 : eventType.hashCode()); result = (prime * result) + ((type == null) ? 0 : type.hashCode()); return result; }
@Test(dataProvider = "types", expectedExceptions = { ClassCacheModificationException.class }) public void addNewUnInitializedTypeThatWasNotKnown(Class<? extends Type> type) throws Exception { String fqn = "class"; when(lookup.findByFQN(fqn)).thenReturn(null); service.lookup = lookup; Type theClass = construct(type, fqn); Events events = service.merge(theClass); Events expected = new Events(); expected.addEvent(new NodeEvent(theClass, NodeEventType.NEW, NodeEventDetails.NOT_INITIALIZED)); assertEvents(events, expected); }
@Test public void handleTwoLevelInstanceAreNotSupported() throws Exception { // that is a class, that has a superclass, that has a superclass String[] fqns = new String[] { "1", "2", "3" }; int[] modifiers = new int[] { 0, 1, 2 }; String[] hashes = new String[] { "a", "b", "c" }; ClassType base = new ClassType(fqns[0], hashes[0], modifiers[0]); ClassType firstSuper = new ClassType(fqns[1], hashes[1], modifiers[1]); ClassType secondSuper = new ClassType(fqns[2], hashes[2], modifiers[2]); base.addSuperClass(firstSuper); firstSuper.addSuperClass(secondSuper); // we assume that nothing is there yet when(lookup.findByFQN(fqns[0])).thenReturn(null); when(lookup.findByFQN(fqns[1])).thenReturn(null); when(lookup.findByFQN(fqns[2])).thenReturn(null); service.lookup = lookup; Events events = service.merge(base); // We do not support multi-level structures! Events expected = new Events(); expected.addEvent(new NodeEvent(base, NodeEventType.NEW, NodeEventDetails.INITIALIZED)); expected.addEvent(new NodeEvent(firstSuper, NodeEventType.NEW, NodeEventDetails.NOT_INITIALIZED)); expected.addEvent(new ReferenceEvent(base, firstSuper, ReferenceType.SUPERCLASS)); assertEvents(events, expected); }
/** * {@inheritDoc} */ @Override public void informNodeChange(NodeEvent event) { if (NodeEventDetails.INITIALIZED.equals(event.getEventDetails())) { // if it's initialized type index it cause he have hash ImmutableType type = event.getType(); for (String hash : type.getHashes()) { storage.put(hash, type); } } else if (NodeEventType.CHANGED.equals(event.getEventType()) && NodeEventDetails.HASH_ADDED.equals(event.getEventDetails())) { // otherwise only index it if there is new hash available ImmutableType type = event.getType(); for (String hash : type.getHashes()) { if (!storage.containsKey(hash)) { storage.put(hash, type); } } } else if (NodeEventType.REMOVED.equals(event.getEventType())) { // if removed kill all links ImmutableType type = event.getType(); for (String hash : type.getHashes()) { storage.remove(hash); } } }
/** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public void informNodeChange(NodeEvent event) { if (NodeEventType.NEW.equals(event.getEventType())) { index((E) event.getType()); } else if (NodeEventType.REMOVED.equals(event.getEventType())) { remove(event.getType()); } }
/** * adds all annotations to this type. * * @param type * to add the annotations to. * @param annotations * the annotations to add. * @param events * write notifications here. */ private void addAnnotations(TypeWithAnnotations type, Set<AnnotationType> annotations, Events events) { for (AnnotationType annotationType : annotations) { AnnotationType lookupUp = getOrAddReferredType(annotationType, events, AnnotationType.class); fireAndSave(new ReferenceEvent((Type) type, lookupUp, ReferenceType.ANNOTATION), events); type.addAnnotation(lookupUp); } }
/** * Fires up the {@link NodeEvent} and saves it to the {@link Events}. * * @param referenceEvent * {@link ReferenceEvent} * @param events * Events to add event to. */ private void fireAndSave(ReferenceEvent referenceEvent, Events events) { events.addEvent(referenceEvent); classCache.informReferenceChange(referenceEvent); }
/** * Removes the given type from the cache by firing the remove event and cleaning all existing * references. * * @param existingType * Existing type. * @param events * {@link Events} */ private void removeDueToTypeChange(Type existingType, Events events) { existingType.removeReferences(); fireAndSave(new NodeEvent(existingType, NodeEventType.REMOVED, null), events); log.warn("Type " + existingType + " removed from the class-cache as it changed the base type."); }
private void assertEventsEmpty(Events events) { assertThat(events, is(not(nullValue()))); assertThat(events.getNodeEvents(), is(empty())); assertThat(events.getReferenceEvents(), is(empty())); }
/** * {@inheritDoc} */ @Override public int hashCode() { final int prime = 31; int result = 1; result = (prime * result) + ((referenceType == null) ? 0 : referenceType.hashCode()); result = (prime * result) + ((from == null) ? 0 : from.hashCode()); result = (prime * result) + ((to == null) ? 0 : to.hashCode()); return result; }
@Test(dataProvider = "types") public void addNewInitializedTypeThatWasNotKnown(Class<? extends Type> type) throws Exception { String fqn = "class"; String hash = "hash"; int modifiers = 0; when(lookup.findByFQN(fqn)).thenReturn(null); service.lookup = lookup; Type theClass = construct(type, fqn, hash, modifiers); Events events = service.merge(theClass); Events expected = new Events(); expected.addEvent(new NodeEvent(theClass, NodeEventType.NEW, NodeEventDetails.INITIALIZED)); assertEvents(events, expected); }
/** * adds all interfaces to the class type. * * @param type * to add the interfaces to. * @param interfaces * the interfaces to add. * @param events * write notifications here. */ private void addInterface(ClassType type, Set<AbstractInterfaceType> interfaces, Events events) { for (AbstractInterfaceType i : interfaces) { AbstractInterfaceType lookupUp = getOrAddReferredType(i, events, AbstractInterfaceType.class); fireAndSave(new ReferenceEvent(type, lookupUp, ReferenceType.REALIZE_INTERFACE), events); type.addInterface(lookupUp); } }
/** * Fires up the {@link NodeEvent} and saves it to the {@link Events}. * * @param nodeEvent * {@link NodeEvent} * @param events * Events to add event to. */ private void fireAndSave(NodeEvent nodeEvent, Events events) { events.addEvent(nodeEvent); classCache.informNodeChange(nodeEvent); }
/** * merges/adds the given entity to the type in the structure. * * @param inputType * the type in the structure or null if not existing * @param givenType * the given entity * @param events * write notifications here. */ private void handleBaseEntity(Type inputType, Type givenType, Events events) { if (null == inputType) { // ADD the new element to the structure fireAndSave(new NodeEvent(givenType, NodeEventType.NEW, NodeEventDetails.INITIALIZED), events); // RESOLVE the references of the type resolveReferences(givenType, events); } else { if ((givenType instanceof AnnotationType) && (inputType instanceof InterfaceType)) { // special case, when annotation is used as interface must be handled in different // way mergeAnnotationAsInterface((AnnotationType) givenType, (InterfaceType) inputType, events); } else { // MERGE the existing element with the information from the given entity merge(inputType, givenType, events); } } }
@Test(dataProvider = "types") public void addNewInitializedTypeThatIsKnownUninitialized(Class<? extends Type> type) throws Exception { String fqn = "class"; String hash = "hash"; int modifiers = 0; Type notInitialized = construct(type, fqn); when(lookup.findByFQN(fqn)).thenReturn(notInitialized); service.lookup = lookup; Type initialized = construct(type, fqn, hash, modifiers); Events events = service.merge(initialized); Events expected = new Events(); expected.addEvent(new NodeEvent(notInitialized, NodeEventType.CHANGED, NodeEventDetails.INITIALIZED)); assertEvents(events, expected); }
/** * Adds all super classes to the class type. * * @param type * to add the super-classes to. * @param superclasses * the super-classes to add. * @param events * write notifications here. */ private void addSuperclass(ClassType type, Set<ClassType> superclasses, Events events) { for (ClassType superClass : superclasses) { ClassType lookupUp = getOrAddReferredType(superClass, events, ClassType.class); fireAndSave(new ReferenceEvent(type, lookupUp, ReferenceType.SUPERCLASS), events); type.addSuperClass(lookupUp); } }
@Test(dataProvider = "types") public void addNewInitializedTypeThatIsKnownInitializedWithDifferentHash(Class<? extends Type> type) throws Exception { String fqn = "class"; String hash = "hash"; String hashStored = "hashStored"; int modifiers = 0; Type storedClass = construct(type, fqn, hashStored, modifiers); when(lookup.findByFQN(fqn)).thenReturn(storedClass); service.lookup = lookup; Type initialized = construct(type, fqn, hash, modifiers); Events events = service.merge(initialized); Events expected = new Events(); expected.addEvent(new NodeEvent(storedClass, NodeEventType.CHANGED, NodeEventDetails.HASH_ADDED)); assertEvents(events, expected); }
/** * adds all super interfaces to the interface type. * * @param type * to add the super interfaces to. * @param superinterfaces * the super interfaces to add. * @param events * write notifications here. */ private void addSuperinterface(InterfaceType type, Set<InterfaceType> superinterfaces, Events events) { for (InterfaceType i : superinterfaces) { InterfaceType lookupUp = getOrAddReferredType(i, events, InterfaceType.class); fireAndSave(new ReferenceEvent(type, lookupUp, ReferenceType.SUPERINTERFACE), events); type.addSuperInterface(lookupUp); } }