public final void notify(final String upstreamDependency, String downstreamDependency) {
    if (downstreamDependency == null) {
      notifyForGenericListener(upstreamDependency);
      return;
    }

    // Handle if the downstream dependency is "class level",  meaning we need to figure out the
    // specific downstream MID this metadata provider wants to update/refresh.
    if (MetadataIdentificationUtils.isIdentifyingClass(downstreamDependency)) {
      // We have not identified an instance-specific downstream MID, so we'll need to calculate an
      // instance-specific downstream MID to retrieve.
      downstreamDependency = resolveDownstreamDependencyIdentifier(upstreamDependency);

      // We skip if the resolution method returns null, as it doesn't want to continue for some
      // reason
      if (downstreamDependency == null) {
        return;
      }

      Assert.isTrue(
          MetadataIdentificationUtils.isIdentifyingInstance(downstreamDependency),
          "An instance-specific downstream MID was required by '"
              + getClass().getName()
              + "' (not '"
              + downstreamDependency
              + "')");

      // We only need to proceed if the downstream dependency relationship is not already
      // registered.
      // It is unusual to register a direct downstream relationship given it costs dependency
      // registration memory and class-level notifications will always occur anyway.
      if (metadataDependencyRegistry
          .getDownstream(upstreamDependency)
          .contains(downstreamDependency)) {
        return;
      }
    }

    // We should now have an instance-specific "downstream dependency" that can be processed by this
    // class
    Assert.isTrue(
        MetadataIdentificationUtils.getMetadataClass(downstreamDependency)
            .equals(MetadataIdentificationUtils.getMetadataClass(getProvidesType())),
        "Unexpected downstream notification for '"
            + downstreamDependency
            + "' to this provider (which uses '"
            + getProvidesType()
            + "'");

    // We no longer notify downstreams here, as the "get" operation with eviction will ensure the
    // main get(String) method below will be fired and it
    // directly notified downstreams as part of that method (BPA 10 Dec 2010)
    metadataService.get(downstreamDependency, true);
  }
 public final String getIdForPhysicalJavaType(final String physicalJavaTypeIdentifier) {
   Assert.isTrue(
       MetadataIdentificationUtils.getMetadataClass(physicalJavaTypeIdentifier)
           .equals(
               MetadataIdentificationUtils.getMetadataClass(
                   PhysicalTypeIdentifier.getMetadataIdentiferType())),
       "Expected a valid physical Java type instance identifier (not '"
           + physicalJavaTypeIdentifier
           + "')");
   JavaType javaType = PhysicalTypeIdentifier.getJavaType(physicalJavaTypeIdentifier);
   ContextualPath path = PhysicalTypeIdentifier.getPath(physicalJavaTypeIdentifier);
   return createLocalIdentifier(javaType, path);
 }
  /**
   * Assists creating a local metadata identification string (MID) from any presented {@link
   * MemberHoldingTypeDetails} implementation. This is achieved by extracting the {@link
   * IdentifiableJavaStructure#getDeclaredByMetadataId()} and converting it into a {@link JavaType}
   * and {@link Path}, then calling {@link #createLocalIdentifier(JavaType, Path)}.
   *
   * @param memberHoldingTypeDetails the member holder from which the declaring type information
   *     should be extracted (required)
   * @return a MID produced by {@link #createLocalIdentifier(JavaType, Path)} for the extracted Java
   *     type in the extract Path (never null)
   */
  protected String getLocalMid(final MemberHoldingTypeDetails memberHoldingTypeDetails) {
    JavaType governorType = memberHoldingTypeDetails.getName();

    // Extract out the metadata provider class (we need this later to extract just the Path it is
    // located in)
    String providesType =
        MetadataIdentificationUtils.getMetadataClass(
            memberHoldingTypeDetails.getDeclaredByMetadataId());
    ContextualPath path =
        PhysicalTypeIdentifierNamingUtils.getPath(
            providesType, memberHoldingTypeDetails.getDeclaredByMetadataId());
    // Produce the local MID we're going to use to make the request
    return createLocalIdentifier(governorType, path);
  }
  public void notifyDownstream(final String upstreamDependency) {
    try {
      metadataLogger.startEvent();

      if (metadataService != null) {
        // First dispatch the fine-grained, instance-specific
        // dependencies.
        Set<String> notifiedDownstreams = new HashSet<String>();
        for (final String downstream : getDownstream(upstreamDependency)) {
          if (metadataLogger.getTraceLevel() > 0) {
            metadataLogger.log(upstreamDependency + " -> " + downstream);
          }
          // No need to ensure upstreamDependency is different from
          // downstream, as that's taken care of in the
          // isValidDependency() method
          try {
            final String responsibleClass =
                MetadataIdentificationUtils.getMetadataClass(downstream);
            metadataLogger.startTimer(responsibleClass);
            metadataService.notify(upstreamDependency, downstream);
          } finally {
            metadataLogger.stopTimer();
          }
          notifiedDownstreams.add(downstream);
        }

        // Next dispatch the coarse-grained, class-specific
        // dependencies.
        // We only do it if the upstream is not class specific, as
        // otherwise we'd have handled class-specific dispatch in
        // previous loop
        if (!MetadataIdentificationUtils.isIdentifyingClass(upstreamDependency)) {
          final String asClass = MetadataIdentificationUtils.getMetadataClassId(upstreamDependency);
          for (final String downstream : getDownstream(asClass)) {
            // We don't notify a downstream if it had a direct
            // instance-specific dependency and was already notified
            // in previous loop
            // We also don't notify if upstream is the same as
            // downstream, as it doesn't make sense to notify
            // yourself of an event
            // (such a condition is only possible if an instance
            // registered to receive class-specific notifications
            // and that instance
            // caused an event to fire)
            if (!notifiedDownstreams.contains(downstream)
                && !upstreamDependency.equals(downstream)) {
              if (metadataLogger.getTraceLevel() > 0) {
                metadataLogger.log(upstreamDependency + " -> " + downstream + " [via class]");
              }
              try {
                final String responsibleClass =
                    MetadataIdentificationUtils.getMetadataClass(downstream);
                metadataLogger.startTimer(responsibleClass);
                metadataService.notify(upstreamDependency, downstream);
              } finally {
                metadataLogger.stopTimer();
              }
            }
          }
        }

        notifiedDownstreams = null;
      }

      // Finally dispatch the general-purpose additional listeners
      for (final MetadataNotificationListener listener : listeners) {
        if (metadataLogger.getTraceLevel() > 1) {
          metadataLogger.log(
              upstreamDependency
                  + " -> "
                  + upstreamDependency
                  + " ["
                  + listener.getClass().getSimpleName()
                  + "]");
        }
        try {
          final String responsibleClass = listener.getClass().getName();
          metadataLogger.startTimer(responsibleClass);
          listener.notify(upstreamDependency, null);
        } finally {
          metadataLogger.stopTimer();
        }
      }
    } finally {
      metadataLogger.stopEvent();
    }
  }
  public void notify(String upstreamDependency, String downstreamDependency) {
    ProjectMetadata projectMetadata = projectOperations.getProjectMetadata();
    if (projectMetadata == null) {
      return;
    }

    if (MetadataIdentificationUtils.isIdentifyingClass(downstreamDependency)) {
      Assert.isTrue(
          MetadataIdentificationUtils.getMetadataClass(upstreamDependency)
              .equals(
                  MetadataIdentificationUtils.getMetadataClass(
                      PhysicalTypeIdentifier.getMetadataIdentiferType())),
          "Expected class-level notifications only for PhysicalTypeIdentifier (not '"
              + upstreamDependency
              + "')");

      ClassOrInterfaceTypeDetails cid =
          typeLocationService.getTypeForIdentifier(upstreamDependency);
      boolean processed = false;
      if (MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), RooJavaType.ROO_GWT_REQUEST)
          != null) {
        ClassOrInterfaceTypeDetails proxy = gwtTypeService.lookupProxyFromRequest(cid);
        if (proxy != null) {
          JavaType typeName = PhysicalTypeIdentifier.getJavaType(proxy.getDeclaredByMetadataId());
          Path typePath = PhysicalTypeIdentifier.getPath(proxy.getDeclaredByMetadataId());
          downstreamDependency = GwtLocatorMetadata.createIdentifier(typeName, typePath);
          processed = true;
        }
      }
      if (!processed
          && MemberFindingUtils.getAnnotationOfType(cid.getAnnotations(), RooJavaType.ROO_GWT_PROXY)
              == null) {
        boolean found = false;
        for (ClassOrInterfaceTypeDetails classOrInterfaceTypeDetails :
            typeLocationService.findClassesOrInterfaceDetailsWithAnnotation(
                RooJavaType.ROO_GWT_PROXY)) {
          AnnotationMetadata annotationMetadata =
              GwtUtils.getFirstAnnotation(
                  classOrInterfaceTypeDetails, GwtUtils.ROO_PROXY_REQUEST_ANNOTATIONS);
          if (annotationMetadata != null) {
            AnnotationAttributeValue<?> attributeValue = annotationMetadata.getAttribute("value");
            if (attributeValue != null) {
              String mirrorName = GwtUtils.getStringValue(attributeValue);
              if (mirrorName != null
                  && cid.getName().getFullyQualifiedTypeName().equals(attributeValue.getValue())) {
                found = true;
                JavaType typeName =
                    PhysicalTypeIdentifier.getJavaType(
                        classOrInterfaceTypeDetails.getDeclaredByMetadataId());
                Path typePath =
                    PhysicalTypeIdentifier.getPath(
                        classOrInterfaceTypeDetails.getDeclaredByMetadataId());
                downstreamDependency = GwtLocatorMetadata.createIdentifier(typeName, typePath);
                break;
              }
            }
          }
        }
        if (!found) {
          return;
        }
      } else if (!processed) {
        // A physical Java type has changed, and determine what the corresponding local metadata
        // identification string would have been
        JavaType typeName = PhysicalTypeIdentifier.getJavaType(upstreamDependency);
        Path typePath = PhysicalTypeIdentifier.getPath(upstreamDependency);
        downstreamDependency = GwtLocatorMetadata.createIdentifier(typeName, typePath);
      }

      // We only need to proceed if the downstream dependency relationship is not already registered
      // (if it's already registered, the event will be delivered directly later on)
      if (metadataDependencyRegistry
          .getDownstream(upstreamDependency)
          .contains(downstreamDependency)) {
        return;
      }
    }

    // We should now have an instance-specific "downstream dependency" that can be processed by this
    // class
    Assert.isTrue(
        MetadataIdentificationUtils.getMetadataClass(downstreamDependency)
            .equals(MetadataIdentificationUtils.getMetadataClass(getProvidesType())),
        "Unexpected downstream notification for '"
            + downstreamDependency
            + "' to this provider (which uses '"
            + getProvidesType()
            + "'");

    metadataService.get(downstreamDependency, true);
  }
 private boolean isNotificationForJavaType(final String mid) {
   return MetadataIdentificationUtils.getMetadataClass(mid)
       .equals(
           MetadataIdentificationUtils.getMetadataClass(
               PhysicalTypeIdentifier.getMetadataIdentiferType()));
 }
  public final MetadataItem get(final String metadataIdentificationString) {
    Assert.isTrue(
        MetadataIdentificationUtils.getMetadataClass(metadataIdentificationString)
            .equals(MetadataIdentificationUtils.getMetadataClass(getProvidesType())),
        "Unexpected request for '"
            + metadataIdentificationString
            + "' to this provider (which uses '"
            + getProvidesType()
            + "'");

    // Remove the upstream dependencies for this instance (we'll be recreating them later, if
    // needed)
    metadataDependencyRegistry.deregisterDependencies(metadataIdentificationString);

    // Compute the identifier for the Physical Type Metadata we're correlated with
    String governorPhysicalTypeIdentifier =
        getGovernorPhysicalTypeIdentifier(metadataIdentificationString);

    // Obtain the physical type
    PhysicalTypeMetadata governorPhysicalTypeMetadata =
        (PhysicalTypeMetadata) metadataService.get(governorPhysicalTypeIdentifier);
    if (governorPhysicalTypeMetadata == null || !governorPhysicalTypeMetadata.isValid()) {
      // We can't get even basic information about the physical type, so abort (the ITD will be
      // deleted by ItdFileDeletionService)
      return null;
    }

    // Flag to indicate whether we'll even try to create this metadata
    boolean produceMetadata = false;

    // Determine if we should generate the metadata on the basis of it containing a trigger
    // annotation
    ClassOrInterfaceTypeDetails cid = null;
    if (governorPhysicalTypeMetadata.getMemberHoldingTypeDetails()
        instanceof ClassOrInterfaceTypeDetails) {
      cid =
          (ClassOrInterfaceTypeDetails) governorPhysicalTypeMetadata.getMemberHoldingTypeDetails();
      // Only create metadata if the type is annotated with one of the metadata triggers
      for (JavaType trigger : metadataTriggers) {
        if (cid.getAnnotation(trigger) != null) {
          produceMetadata = true;
          break;
        }
      }
    }

    // Fall back to ignoring trigger annotations
    if (ignoreTriggerAnnotations) {
      produceMetadata = true;
    }

    // Cancel production if the governor type details are required, but aren't available
    if (dependsOnGovernorTypeDetailAvailability && cid == null) {
      produceMetadata = false;
    }

    // Cancel production if the governor is not a class, and the subclass only wants to know about
    // classes
    if (cid != null
        && dependsOnGovernorBeingAClass
        && cid.getPhysicalTypeCategory() != PhysicalTypeCategory.CLASS) {
      produceMetadata = false;
    }

    String itdFilename = governorPhysicalTypeMetadata.getItdCanonicalPath(this);
    if (!produceMetadata && isGovernor(cid) && fileManager.exists(itdFilename)) {
      // We don't seem to want metadata anymore, yet the ITD physically exists, so get rid of it
      // This might be because the trigger annotation has been removed, the governor is missing a
      // class declaration, etc.
      deleteItd(
          metadataIdentificationString,
          itdFilename,
          "not required for governor " + cid.getName(),
          true);
      return null;
    }

    if (produceMetadata) {
      // This type contains an annotation we were configured to detect, or there is an ITD (which
      // may need deletion), so we need to produce the metadata
      JavaType aspectName = governorPhysicalTypeMetadata.getItdJavaType(this);
      ItdTypeDetailsProvidingMetadataItem metadata =
          getMetadata(
              metadataIdentificationString, aspectName, governorPhysicalTypeMetadata, itdFilename);

      // There is no requirement to register a direct connection with the physical type and this
      // metadata because changes will
      // trickle down via the class-level notification registered by convention by
      // AbstractItdMetadataProvider subclasses (BPA 10 Dec 2010)

      if (metadata == null || !metadata.isValid()) {
        // The metadata couldn't be created properly
        deleteItd(metadataIdentificationString, itdFilename, "", false);
        return null;
      }

      // By this point we have a valid MetadataItem, but it might not contain any members for the
      // resulting ITD etc

      // Handle the management of the ITD file
      boolean deleteItdFile = false;
      ItdTypeDetails itdTypeDetails = metadata.getMemberHoldingTypeDetails();

      if (itdTypeDetails == null) {
        // The ITD has no members
        deleteItdFile = true;
      }

      if (!deleteItdFile) {
        // We have some members in the ITD, so decide if we're to write something to disk
        ItdSourceFileComposer itdSourceFileComposer =
            new ItdSourceFileComposer(metadata.getMemberHoldingTypeDetails());

        // Decide whether the get an ITD on-disk based on whether there is physical content to write
        if (itdSourceFileComposer.isContent()) {
          // We have content to write
          itdDiscoveryService.addItdTypeDetails(itdTypeDetails);
          String itd = itdSourceFileComposer.getOutput();
          fileManager.createOrUpdateTextFileIfRequired(itdFilename, itd, false);
        } else {
          // We don't have content to write
          deleteItdFile = true;
        }
      }

      if (deleteItdFile) {
        deleteItd(metadataIdentificationString, itdFilename, null, false);
      }

      // Eagerly notify that the metadata has been updated; this also registers the metadata hash
      // code in the superclass' cache to avoid
      // unnecessary subsequent notifications if it hasn't changed
      notifyIfRequired(metadata);

      return metadata;
    }
    return null;
  }