/** * Processes all types in the class cache in order to add instrumentation points for the given * agent configuration. Instrumentation points added will be created based on given * {@link IInstrumentationApplier}s. * * @param agentConfiguration * configuration to use * @param appliers * Collection of {@link IInstrumentationApplier}s to process types against. * @return Returns collection of class types to which the instrumentation points have been * added. */ public Collection<? extends ImmutableClassType> addInstrumentationPoints(AgentConfig agentConfiguration, Collection<IInstrumentationApplier> appliers) { Collection<ImmutableClassType> results = new ArrayList<>(0); for (IInstrumentationApplier applier : appliers) { AbstractClassSensorAssignment<?> assignment = applier.getSensorAssignment(); Collection<? extends ImmutableType> types; if (null != assignment) { types = searchNarrower.narrowByClassSensorAssignment(classCache, assignment); } else { types = classCache.getLookupService().findAll(); } Collection<? extends ImmutableClassType> instrumented = addInstrumentationPoints(types, agentConfiguration, appliers); if (CollectionUtils.isNotEmpty(instrumented)) { results.addAll(instrumented); } } return results; }
/** * Collects all realizing classes that implement given interface or any of its sub-interfaces * and adds them to the given results list. This method is recursive. * * @param results * List to store classes to. * @param interfaceType * Type to check. */ private void collectClassesFromInterfaceAndSubInterfaces(Collection<ImmutableClassType> results, ImmutableInterfaceType interfaceType) { for (ImmutableClassType classType : interfaceType.getImmutableRealizingClasses()) { if (classType.isInitialized()) { results.add(classType); } collectClassesFromSubClasses(results, classType); } for (ImmutableInterfaceType superInterfaceType : interfaceType.getImmutableSubInterfaces()) { collectClassesFromInterfaceAndSubInterfaces(results, superInterfaceType); } }
/** * Helps in the search for the {@link ImmutableClassType} that fit the given * {@link AbstractClassSensorAssignment}. * <p> * Search order is following: * <p> * 1. If direct class/interface/super-class name is given, then by name <br> * 2. If annotation is given, then by annotation<br> * 3. If nothing of above, then by the wild card name search. * * @param classCache * {@link ClassCache} to look in. * @param classSensorAssignment * {@link AbstractClassSensorAssignment} * @return All initialized {@link ImmutableClassType} that might match given * {@link AbstractClassSensorAssignment}. Note that this is only narrow process, full * check must be performed on the returned results. */ public Collection<? extends ImmutableClassType> narrowByClassSensorAssignment(ClassCache classCache, AbstractClassSensorAssignment<?> classSensorAssignment) { if (!WildcardMatchPattern.isPattern(classSensorAssignment.getClassName())) { // if we don't have a pattern that just load return narrowByNameSearch(classCache, classSensorAssignment.getClassName(), classSensorAssignment.isInterf(), classSensorAssignment.isSuperclass()); } if (null != classSensorAssignment.getAnnotation()) { return narrowByAnnotationSearch(classCache, classSensorAssignment.getAnnotation()); } // if nothing works then we have wild-card search in name return narrowByNameSearch(classCache, classSensorAssignment.getClassName(), classSensorAssignment.isInterf(), classSensorAssignment.isSuperclass()); }
/** * Collects all realizing classes that are sub-class of given class type or any of its * sub-classes and adds them to the given results list. This method is recursive. * * @param results * List to store classes to. * @param classType * Type to check. */ private void collectClassesFromSubClasses(Collection<ImmutableClassType> results, ImmutableClassType classType) { for (ImmutableClassType subClassType : classType.getImmutableSubClasses()) { if (subClassType.isInitialized()) { results.add(subClassType); } collectClassesFromSubClasses(results, subClassType); } }
@Test public void searchNarrowAddNothingWhenEmpty() throws Exception { doReturn(assignment).when(instrumentationApplier).getSensorAssignment(); doReturn(Collections.emptyList()).when(searchNarrower).narrowByClassSensorAssignment(classCache, assignment); Collection<? extends ImmutableClassType> result = instrumentation.addInstrumentationPoints(agentConfiguration, Collections.singleton(instrumentationApplier)); // assert result assertThat(result, is(empty())); // not touching the write lock verify(classCache, times(0)).executeWithWriteLock(Matchers.<Callable<?>> any()); verify(instrumentationApplier, times(1)).getSensorAssignment(); verifyNoMoreInteractions(instrumentationApplier); }
collectClassesFromInterfaceAndSubInterfaces(results, interfaceType); collectClassesFromSubClasses(results, superClassType);
@Test public void searchNarrowAddNothingForNonInitializedType() throws Exception { when(classType.isInitialized()).thenReturn(false); doReturn(assignment).when(instrumentationApplier).getSensorAssignment(); doReturn(Collections.singleton(classType)).when(searchNarrower).narrowByClassSensorAssignment(classCache, assignment); Collection<? extends ImmutableClassType> result = instrumentation.addInstrumentationPoints(agentConfiguration, Collections.singleton(instrumentationApplier)); // assert result assertThat(result, is(empty())); // must be write lock verify(classCache, times(1)).executeWithWriteLock(Matchers.<Callable<?>> any()); verify(instrumentationApplier, times(1)).getSensorAssignment(); verifyNoMoreInteractions(instrumentationApplier); }
results.add(immutableClassType); collectClassesFromSubClasses(results, immutableClassType); } else if (immutableType.isInterface()) { ImmutableInterfaceType immutableInterfaceType = immutableType.castToInterface(); collectClassesFromInterfaceAndSubInterfaces(results, immutableInterfaceType);
@Test public void byDirectName() { String className = "info.novatec.MyClass"; when(methodSensorAssignment.getClassName()).thenReturn(className); when(methodSensorAssignment.isInterf()).thenReturn(false); when(methodSensorAssignment.isSuperclass()).thenReturn(false); ClassType classType = new ClassType(className, "hash", 0); doReturn(Collections.singleton(classType)).when(lookup).findClassTypesByPattern(eq(className), anyBoolean()); Collection<? extends ImmutableClassType> result = narrower.narrowByClassSensorAssignment(classCache, methodSensorAssignment); assertThat(result, hasSize(1)); assertThat(result.iterator().next(), is((ImmutableClassType) classType)); verify(lookup, times(1)).findClassTypesByPattern(className, true); verifyNoMoreInteractions(lookup); }
@Test public void removedAssignmentNoChange() throws RemoteException { Collection<ClassType> types = ImmutableList.of(classTypeOne, classTypeTwo); doReturn(instrumentationApplier).when(configurationResolver).getInstrumentationApplier(sensorAssignment, environment); doReturn(types).when(classCacheSearchNarrower).narrowByClassSensorAssignment(classCache, sensorAssignment); doReturn(Collections.emptyList()).when(instrumentationService).removeInstrumentationPoints(eq(types), Matchers.<Collection<IInstrumentationApplier>> any()); doReturn(Collections.singleton(sensorAssignment)).when(event).getRemovedSensorAssignments(); job.setProfileUpdateEvent(event); job.run(); ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class); verify(instrumentationService, times(1)).removeInstrumentationPoints(eq(types), captor.capture()); assertThat((Collection<IInstrumentationApplier>) captor.getValue(), hasSize(1)); assertThat(((Collection<IInstrumentationApplier>) captor.getValue()).iterator().next(), is(instrumentationApplier)); verifyNoMoreInteractions(instrumentationService); verifyZeroInteractions(environment, eventPublisher); } }
@Test public void removedAssignmentNoChange() { Collection<ClassType> types = Collections.singleton(classType); doReturn(instrumentationApplier).when(configurationResolver).getInstrumentationApplier(sensorAssignment, environment); doReturn(types).when(classCacheSearchNarrower).narrowByClassSensorAssignment(classCache, sensorAssignment); doReturn(Collections.emptyList()).when(instrumentationService).removeInstrumentationPoints(eq(types), Matchers.<Collection<IInstrumentationApplier>> any()); doReturn(Collections.singletonList(sensorAssignment)).when(event).getRemovedSensorAssignments(functionalAssignmentFactory); job.setEnvironmentUpdateEvent(event); job.run(); verify(configurationHolder, times(1)).update(updateEnvironment, PLATFORM_ID); ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class); verify(instrumentationService, times(1)).removeInstrumentationPoints(types, Collections.singleton(instrumentationApplier)); verifyNoMoreInteractions(instrumentationService); verifyZeroInteractions(environment, agentConfiguration, eventPublisher); } }
@Test public void addedAssignment() throws RemoteException, BusinessException { Collection<ClassType> types = Collections.singleton(classType); doReturn(instrumentationApplier).when(configurationResolver).getInstrumentationApplier(sensorAssignment, environment); doReturn(types).when(classCacheSearchNarrower).narrowByClassSensorAssignment(classCache, sensorAssignment); doReturn(types).when(instrumentationService).addInstrumentationPoints(eq(types), eq(agentConfiguration), Matchers.<Collection<IInstrumentationApplier>> any()); doReturn(Collections.singletonList(sensorAssignment)).when(event).getAddedSensorAssignments(functionalAssignmentFactory); job.setEnvironmentUpdateEvent(event); job.run(); verify(configurationHolder, times(1)).update(updateEnvironment, PLATFORM_ID); ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class); verify(instrumentationService, times(1)).addInstrumentationPoints(eq(types), eq(agentConfiguration), captor.capture()); assertThat((Collection<IInstrumentationApplier>) captor.getValue(), hasSize(1)); assertThat(((Collection<IInstrumentationApplier>) captor.getValue()).iterator().next(), is(instrumentationApplier)); ArgumentCaptor<ClassInstrumentationChangedEvent> eventCaptor = ArgumentCaptor.forClass(ClassInstrumentationChangedEvent.class); verify(eventPublisher).publishEvent(eventCaptor.capture()); assertThat(eventCaptor.getValue().getAgentId(), is(equalTo(PLATFORM_ID))); assertThat(eventCaptor.getValue().getInstrumentationDefinitions(), contains(org.hamcrest.Matchers.<InstrumentationDefinition> hasProperty("className", equalTo("fqn")))); verifyNoMoreInteractions(eventPublisher); verifyZeroInteractions(environment, agentConfiguration); }
/** * Process the added assignments. New instrumentation points will be added to all the classes in * the class cache that fit to the given assignments. * * @param classSensorAssignments * Collection of added {@link AbstractClassSensorAssignment}s. * @return Returns a {@link Collection} of {@link ImmutableClassType} which have been added. */ protected Collection<ImmutableClassType> processAddedAssignments(Collection<? extends AbstractClassSensorAssignment<?>> classSensorAssignments) { Collection<ImmutableClassType> changedClassTypes = new ArrayList<>(); // process all class sensor assignments for adding for (AbstractClassSensorAssignment<?> assignment : classSensorAssignments) { // narrow the search Collection<? extends ImmutableClassType> classTypes = classCacheSearchNarrower.narrowByClassSensorAssignment(getClassCache(), assignment); // get the applier IInstrumentationApplier instrumentationApplier = configurationResolver.getInstrumentationApplier(assignment, getEnvironment()); // execute Collection<? extends ImmutableClassType> instrumentedClassTypes = getClassCache().getInstrumentationService().addInstrumentationPoints(classTypes, getAgentConfiguration(), Collections.singleton(instrumentationApplier)); changedClassTypes.addAll(instrumentedClassTypes); } return changedClassTypes; }
@Test public void searchNarrowAddNothingForNonClassTypes() throws Exception { AnnotationType annotationType = new AnnotationType(""); InterfaceType interfaceType = new InterfaceType(""); List<Type> types = new ArrayList<>(); types.add(annotationType); types.add(interfaceType); doReturn(assignment).when(instrumentationApplier).getSensorAssignment(); doReturn(types).when(searchNarrower).narrowByClassSensorAssignment(classCache, assignment); Collection<? extends ImmutableClassType> result = instrumentation.addInstrumentationPoints(agentConfiguration, Collections.singleton(instrumentationApplier)); // assert result assertThat(result, is(empty())); // must be write lock verify(classCache, times(1)).executeWithWriteLock(Matchers.<Callable<?>> any()); verify(instrumentationApplier, times(1)).getSensorAssignment(); verifyNoMoreInteractions(instrumentationApplier); } }
@Test public void searchNarrowAddNothingWhenInstrumenterDoesNotAdd() throws Exception { when(classType.isClass()).thenReturn(true); when(classType.castToClass()).thenReturn(classType); when(classType.isInitialized()).thenReturn(true); when(instrumentationApplier.addInstrumentationPoints(agentConfiguration, classType)).thenReturn(false); doReturn(assignment).when(instrumentationApplier).getSensorAssignment(); doReturn(Collections.singleton(classType)).when(searchNarrower).narrowByClassSensorAssignment(classCache, assignment); Collection<? extends ImmutableClassType> result = instrumentation.addInstrumentationPoints(agentConfiguration, Collections.singleton(instrumentationApplier)); // assert result assertThat((Collection<ClassType>) result, is(empty())); // must be write lock verify(classCache, times(1)).executeWithWriteLock(Matchers.<Callable<?>> any()); verify(instrumentationApplier, times(1)).addInstrumentationPoints(agentConfiguration, classType); verify(instrumentationApplier, times(1)).getSensorAssignment(); verifyNoMoreInteractions(instrumentationApplier); }
@Test public void searchNarrowAdd() throws Exception { when(classType.isClass()).thenReturn(true); when(classType.castToClass()).thenReturn(classType); when(classType.isInitialized()).thenReturn(true); when(instrumentationApplier.addInstrumentationPoints(agentConfiguration, classType)).thenReturn(true); doReturn(assignment).when(instrumentationApplier).getSensorAssignment(); doReturn(Collections.singleton(classType)).when(searchNarrower).narrowByClassSensorAssignment(classCache, assignment); Collection<? extends ImmutableClassType> result = instrumentation.addInstrumentationPoints(agentConfiguration, Collections.singleton(instrumentationApplier)); // assert result assertThat((Collection<ClassType>) result, hasItem(classType)); // must be write lock verify(classCache, times(1)).executeWithWriteLock(Matchers.<Callable<?>> any()); verify(instrumentationApplier, times(1)).addInstrumentationPoints(agentConfiguration, classType); verify(instrumentationApplier, times(1)).getSensorAssignment(); verifyNoMoreInteractions(instrumentationApplier); }
@Test public void initializedByInterface() { String interfaceName = "info.novatec.MyClass"; when(methodSensorAssignment.getClassName()).thenReturn(interfaceName); when(methodSensorAssignment.isInterf()).thenReturn(true); when(methodSensorAssignment.isSuperclass()).thenReturn(false); InterfaceType interfaceType = new InterfaceType(interfaceName); ClassType classType = new ClassType("initialized", "hash", 0); classType.addInterface(interfaceType); doReturn(Collections.singleton(interfaceType)).when(lookup).findInterfaceTypesByPattern(eq(interfaceName), anyBoolean()); Collection<? extends ImmutableClassType> result = narrower.narrowByClassSensorAssignment(classCache, methodSensorAssignment); assertThat(result, hasSize(1)); assertThat(result.iterator().next(), is((ImmutableClassType) classType)); verify(lookup, times(1)).findInterfaceTypesByPattern(interfaceName, false); verifyNoMoreInteractions(lookup); }
@Test public void initializedBySuperClass() { String superClassName = "info.novatec.MyClass"; when(methodSensorAssignment.getClassName()).thenReturn(superClassName); when(methodSensorAssignment.isInterf()).thenReturn(false); when(methodSensorAssignment.isSuperclass()).thenReturn(true); ClassType superClassType = new ClassType(superClassName); ClassType classType = new ClassType("initialized", "hash", 0); classType.addSuperClass(superClassType); doReturn(Collections.singleton(superClassType)).when(lookup).findClassTypesByPattern(eq(superClassName), anyBoolean()); Collection<? extends ImmutableClassType> result = narrower.narrowByClassSensorAssignment(classCache, methodSensorAssignment); assertThat(result, hasSize(1)); assertThat(result.iterator().next(), is((ImmutableClassType) classType)); verify(lookup, times(1)).findClassTypesByPattern(superClassName, false); verifyNoMoreInteractions(lookup); }
@Test public void nonInitializedBySuperClass() { String superClassName = "info.novatec.MyClass"; when(methodSensorAssignment.getClassName()).thenReturn(superClassName); when(methodSensorAssignment.isInterf()).thenReturn(false); when(methodSensorAssignment.isSuperclass()).thenReturn(true); ClassType superClassType = new ClassType(superClassName); ClassType classType = new ClassType("non-initialized"); classType.addSuperClass(superClassType); doReturn(Collections.singleton(superClassType)).when(lookup).findClassTypesByPattern(eq(superClassName), anyBoolean()); Collection<? extends ImmutableClassType> result = narrower.narrowByClassSensorAssignment(classCache, methodSensorAssignment); assertThat(result, is(empty())); verify(lookup, times(1)).findClassTypesByPattern(superClassName, false); verifyNoMoreInteractions(lookup); }
@Test public void initializedByAnnotation() { String className = "*"; String annotationName = "info.novatec.MyAnnotation"; when(methodSensorAssignment.getClassName()).thenReturn(className); when(methodSensorAssignment.isInterf()).thenReturn(false); when(methodSensorAssignment.isSuperclass()).thenReturn(false); when(methodSensorAssignment.getAnnotation()).thenReturn(annotationName); AnnotationType annotationType = new AnnotationType(annotationName); ClassType classType = new ClassType("initialized", "hash", 0); classType.addAnnotation(annotationType); doReturn(Collections.singleton(annotationType)).when(lookup).findAnnotationTypesByPattern(eq(annotationName), anyBoolean()); Collection<? extends ImmutableClassType> result = narrower.narrowByClassSensorAssignment(classCache, methodSensorAssignment); assertThat(result, hasSize(1)); assertThat(result.iterator().next(), is((ImmutableClassType) classType)); verify(lookup, times(1)).findAnnotationTypesByPattern(annotationName, false); verifyNoMoreInteractions(lookup); }