/** * Basic reference counter implementation that uses a Guava WeakInterner instance to manage * instances of the counter. * * <p>Calling acquire(V) returns the interned copy of the val object. Objects used need to properly * adhere to provide override impls of equals and hashcode to ensure they'll be interned properly. * * <p>Calling release releases the interned object and decrements the reference counter. */ public final class ReferenceCounter<V> { private final Monitor monitor = new Monitor(); private final Interner<CountingReference<V>> interner = Interners.newWeakInterner(); private CountingReference<V> intern(V s) { return interner.intern(ref(s)); } public CountingReference<V> acquire(V val) { monitor.enter(); try { return intern(val).increment(); } finally { monitor.leave(); } } public void release(V val) { monitor.enter(); try { intern(val).decrement(); } finally { monitor.leave(); } } }
/** Functionality to deserialize loaded packages. */ public class PackageDeserializer { private static final Logger LOG = Logger.getLogger(PackageDeserializer.class.getName()); /** Provides the deserializer with tools it needs to build a package from its serialized form. */ public interface PackageDeserializationEnvironment { /** Converts the serialized package's path string into a {@link Path} object. */ Path getPath(String buildFilePath); /** Returns a {@link RuleClass} object for the serialized rule. */ RuleClass getRuleClass(Build.Rule rulePb, Location ruleLocation); /** Description of what rule attributes of each rule should be deserialized. */ AttributesToDeserialize attributesToDeserialize(); } /** * A class that defines what attributes to keep after deserialization. Note that all attributes of * type label are kept in order to navigate between dependencies. * * <p>If {@code addSyntheticAttributeHash} is {@code true}, a synthetic attribute is added to each * Rule that contains a stable hash of the entire serialized rule for the sake of permitting * equality comparisons that respect the attributes that were dropped according to {@code * attributesToKeep}. */ public static class AttributesToDeserialize { private final boolean addSyntheticAttributeHash; private final Predicate<String> shouldKeepAttributeWithName; public AttributesToDeserialize( boolean addSyntheticAttributeHash, Predicate<String> shouldKeepAttributeWithName) { this.addSyntheticAttributeHash = addSyntheticAttributeHash; this.shouldKeepAttributeWithName = shouldKeepAttributeWithName; } public boolean includeAttribute(String attr) { return shouldKeepAttributeWithName.apply(attr); } } public static final AttributesToDeserialize DESERIALIZE_ALL_ATTRS = new AttributesToDeserialize(false, Predicates.<String>alwaysTrue()); // Workaround for Java serialization making it tough to pass in a deserialization environment // manually. // volatile is needed to ensure that the objects are published safely. // TODO(bazel-team): Subclass ObjectOutputStream to pass this through instead. public static volatile PackageDeserializationEnvironment defaultPackageDeserializationEnvironment; // Cache label deserialization across all instances- PackgeDeserializers need to be created on // demand due to initialiation constraints wrt the setting of static members. private static final Interner<Label> LABEL_INTERNER = Interners.newWeakInterner(); /** Class encapsulating state for a single package deserialization. */ private static class DeserializationContext { private final Package.Builder packageBuilder; private DeserializationContext(Package.Builder packageBuilder) { this.packageBuilder = packageBuilder; } } private final PackageDeserializationEnvironment packageDeserializationEnvironment; /** * Creates a {@link PackageDeserializer} using {@link #defaultPackageDeserializationEnvironment}. */ public PackageDeserializer() { this.packageDeserializationEnvironment = defaultPackageDeserializationEnvironment; } public PackageDeserializer(PackageDeserializationEnvironment packageDeserializationEnvironment) { this.packageDeserializationEnvironment = Preconditions.checkNotNull(packageDeserializationEnvironment); } private static ParsedAttributeValue deserializeAttribute( Type<?> expectedType, Build.Attribute attrPb) throws PackageDeserializationException { Object value = deserializeAttributeValue(expectedType, attrPb); return new ParsedAttributeValue( attrPb.hasExplicitlySpecified() && attrPb.getExplicitlySpecified(), value); } private static void deserializeInputFile( DeserializationContext context, Build.SourceFile sourceFile) throws PackageDeserializationException { InputFile inputFile; try { inputFile = context.packageBuilder.createInputFile( deserializeLabel(sourceFile.getName()).getName(), EmptyLocation.INSTANCE); } catch (GeneratedLabelConflict e) { throw new PackageDeserializationException(e); } if (!sourceFile.getVisibilityLabelList().isEmpty() || sourceFile.hasLicense()) { context.packageBuilder.setVisibilityAndLicense( inputFile, PackageFactory.getVisibility(deserializeLabels(sourceFile.getVisibilityLabelList())), deserializeLicense(sourceFile.getLicense())); } } private static void deserializePackageGroup( DeserializationContext context, Build.PackageGroup packageGroupPb) throws PackageDeserializationException { List<String> specifications = new ArrayList<>(); for (String containedPackage : packageGroupPb.getContainedPackageList()) { specifications.add("//" + containedPackage); } try { context.packageBuilder.addPackageGroup( deserializeLabel(packageGroupPb.getName()).getName(), specifications, deserializeLabels(packageGroupPb.getIncludedPackageGroupList()), NullEventHandler.INSTANCE, // TODO(bazel-team): Handle errors properly EmptyLocation.INSTANCE); } catch (LabelSyntaxException | Package.NameConflictException e) { throw new PackageDeserializationException(e); } } private void deserializeRule(DeserializationContext context, Build.Rule rulePb) throws PackageDeserializationException, InterruptedException { Location ruleLocation = EmptyLocation.INSTANCE; RuleClass ruleClass = packageDeserializationEnvironment.getRuleClass(rulePb, ruleLocation); Map<String, ParsedAttributeValue> attributeValues = new HashMap<>(); AttributesToDeserialize attrToDeserialize = packageDeserializationEnvironment.attributesToDeserialize(); Hasher hasher = Hashing.md5().newHasher(); for (Build.Attribute attrPb : rulePb.getAttributeList()) { Type<?> type = ruleClass.getAttributeByName(attrPb.getName()).getType(); attributeValues.put(attrPb.getName(), deserializeAttribute(type, attrPb)); if (attrToDeserialize.addSyntheticAttributeHash) { // TODO(bazel-team): This might give false positives because of explicit vs implicit. hasher.putBytes(attrPb.toByteArray()); } } AttributeContainerWithoutLocation attributeContainer = new AttributeContainerWithoutLocation(ruleClass, hasher.hash()); Label ruleLabel = deserializeLabel(rulePb.getName()); try { Rule rule = createRuleWithParsedAttributeValues( ruleClass, ruleLabel, context.packageBuilder, ruleLocation, attributeValues, NullEventHandler.INSTANCE, attributeContainer); context.packageBuilder.addRule(rule); // Remove the attribute after it is added to package in order to pass the validations // and be able to compute all the outputs. if (attrToDeserialize != DESERIALIZE_ALL_ATTRS) { for (String attrName : attributeValues.keySet()) { Attribute attribute = ruleClass.getAttributeByName(attrName); if (!(attrToDeserialize.shouldKeepAttributeWithName.apply(attrName) || BuildType.isLabelType(attribute.getType()))) { attributeContainer.clearIfNotLabel(attrName); } } } Preconditions.checkState(!rule.containsErrors()); } catch (NameConflictException | LabelSyntaxException e) { throw new PackageDeserializationException(e); } } /** "Empty" location implementation, all methods should return non-null, but empty, values. */ private static class EmptyLocation extends Location { private static final EmptyLocation INSTANCE = new EmptyLocation(); private static final PathFragment DEV_NULL = new PathFragment("/dev/null"); private static final LineAndColumn EMPTY_LINE_AND_COLUMN = new LineAndColumn(0, 0); private EmptyLocation() { super(0, 0); } @Override public PathFragment getPath() { return DEV_NULL; } @Override public LineAndColumn getStartLineAndColumn() { return EMPTY_LINE_AND_COLUMN; } @Override public LineAndColumn getEndLineAndColumn() { return EMPTY_LINE_AND_COLUMN; } @Override public int hashCode() { return 42; } @Override public boolean equals(Object other) { return other instanceof EmptyLocation; } } /** Exception thrown when something goes wrong during package deserialization. */ public static class PackageDeserializationException extends Exception { private PackageDeserializationException(String message) { super(message); } private PackageDeserializationException(String message, Exception reason) { super(message, reason); } private PackageDeserializationException(Exception reason) { super(reason); } } private static Label deserializeLabel(String labelName) throws PackageDeserializationException { try { return LABEL_INTERNER.intern(Label.parseAbsolute(labelName)); } catch (LabelSyntaxException e) { throw new PackageDeserializationException( "Invalid label '" + labelName + "':" + e.getMessage(), e); } } private static List<Label> deserializeLabels(List<String> labelNames) throws PackageDeserializationException { ImmutableList.Builder<Label> result = ImmutableList.builder(); for (String labelName : labelNames) { result.add(deserializeLabel(labelName)); } return result.build(); } private static License deserializeLicense(Build.License licensePb) throws PackageDeserializationException { List<String> licenseStrings = new ArrayList<>(); licenseStrings.addAll(licensePb.getLicenseTypeList()); for (String exception : licensePb.getExceptionList()) { licenseStrings.add("exception=" + exception); } try { return License.parseLicense(licenseStrings); } catch (LicenseParsingException e) { throw new PackageDeserializationException(e); } } private static Set<DistributionType> deserializeDistribs(List<String> distributions) throws PackageDeserializationException { try { return License.parseDistributions(distributions); } catch (LicenseParsingException e) { throw new PackageDeserializationException(e); } } private static TriState deserializeTriStateValue(String value) throws PackageDeserializationException { if (value.equals("yes")) { return TriState.YES; } else if (value.equals("no")) { return TriState.NO; } else if (value.equals("auto")) { return TriState.AUTO; } else { throw new PackageDeserializationException( String.format("Invalid tristate value: '%s'", value)); } } private static List<FilesetEntry> deserializeFilesetEntries(List<Build.FilesetEntry> filesetPbs) throws PackageDeserializationException { ImmutableList.Builder<FilesetEntry> result = ImmutableList.builder(); for (Build.FilesetEntry filesetPb : filesetPbs) { Label srcLabel = deserializeLabel(filesetPb.getSource()); List<Label> files = filesetPb.getFilesPresent() ? deserializeLabels(filesetPb.getFileList()) : null; List<String> excludes = filesetPb.getExcludeList().isEmpty() ? null : ImmutableList.copyOf(filesetPb.getExcludeList()); String destDir = filesetPb.getDestinationDirectory(); FilesetEntry.SymlinkBehavior symlinkBehavior = pbToSymlinkBehavior(filesetPb.getSymlinkBehavior()); String stripPrefix = filesetPb.hasStripPrefix() ? filesetPb.getStripPrefix() : null; result.add( new FilesetEntry(srcLabel, files, excludes, destDir, symlinkBehavior, stripPrefix)); } return result.build(); } /** * Deserialize a package from its representation as a protocol message. The inverse of {@link * PackageSerializer#serialize}. * * @throws IOException * @throws InterruptedException */ private void deserializeInternal( Build.Package packagePb, StoredEventHandler eventHandler, Package.Builder builder, InputStream in) throws PackageDeserializationException, IOException, InterruptedException { Path buildFile = packageDeserializationEnvironment.getPath(packagePb.getBuildFilePath()); Preconditions.checkNotNull(buildFile); DeserializationContext context = new DeserializationContext(builder); builder.setFilename(buildFile); if (packagePb.hasDefaultVisibilitySet() && packagePb.getDefaultVisibilitySet()) { builder.setDefaultVisibility( PackageFactory.getVisibility( deserializeLabels(packagePb.getDefaultVisibilityLabelList()))); } // It's important to do this after setting the default visibility, since that implicitly sets // this bit to true builder.setDefaultVisibilitySet(packagePb.getDefaultVisibilitySet()); if (packagePb.hasDefaultTestonly()) { builder.setDefaultTestonly(packagePb.getDefaultTestonly()); } if (packagePb.hasDefaultDeprecation()) { builder.setDefaultDeprecation(packagePb.getDefaultDeprecation()); } builder.setDefaultCopts(packagePb.getDefaultCoptList()); if (packagePb.hasDefaultHdrsCheck()) { builder.setDefaultHdrsCheck(packagePb.getDefaultHdrsCheck()); } if (packagePb.hasDefaultLicense()) { builder.setDefaultLicense(deserializeLicense(packagePb.getDefaultLicense())); } builder.setDefaultDistribs(deserializeDistribs(packagePb.getDefaultDistribList())); for (String subinclude : packagePb.getSubincludeLabelList()) { Label label = deserializeLabel(subinclude); builder.addSubinclude(label, null); } ImmutableList.Builder<Label> skylarkFileDependencies = ImmutableList.builder(); for (String skylarkFile : packagePb.getSkylarkLabelList()) { skylarkFileDependencies.add(deserializeLabel(skylarkFile)); } builder.setSkylarkFileDependencies(skylarkFileDependencies.build()); MakeEnvironment.Builder makeEnvBuilder = new MakeEnvironment.Builder(); for (Build.MakeVar makeVar : packagePb.getMakeVariableList()) { for (Build.MakeVarBinding binding : makeVar.getBindingList()) { makeEnvBuilder.update( makeVar.getName(), binding.getValue(), binding.getPlatformSetRegexp()); } } builder.setMakeEnv(makeEnvBuilder); for (Build.Event event : packagePb.getEventList()) { deserializeEvent(eventHandler, event); } if (packagePb.hasContainsErrors() && packagePb.getContainsErrors()) { builder.setContainsErrors(); } builder.setWorkspaceName(packagePb.getWorkspaceName()); deserializeTargets(in, context); } private void deserializeTargets(InputStream in, DeserializationContext context) throws IOException, PackageDeserializationException, InterruptedException { Build.TargetOrTerminator tot; while (!(tot = Build.TargetOrTerminator.parseDelimitedFrom(in)).getIsTerminator()) { Build.Target target = tot.getTarget(); switch (target.getType()) { case SOURCE_FILE: deserializeInputFile(context, target.getSourceFile()); break; case PACKAGE_GROUP: deserializePackageGroup(context, target.getPackageGroup()); break; case RULE: deserializeRule(context, target.getRule()); break; default: throw new IllegalStateException("Unexpected Target type: " + target.getType()); } } } /** * Deserializes a {@link Package} from {@code in}. The inverse of {@link * PackageSerializer#serialize}. * * <p>Expects {@code in} to contain a single {@link * com.google.devtools.build.lib.query2.proto.proto2api.Build.Package} message followed by a * series of {@link com.google.devtools.build.lib.query2.proto.proto2api.Build.TargetOrTerminator} * messages encoding the associated targets. * * @param in stream to read from * @return a new {@link Package} as read from {@code in} * @throws PackageDeserializationException on failures deserializing the input * @throws IOException on failures reading from {@code in} * @throws InterruptedException */ public Package deserialize(InputStream in) throws PackageDeserializationException, IOException, InterruptedException { try { return deserializeInternal(in); } catch (PackageDeserializationException | RuntimeException e) { LOG.log(Level.WARNING, "Failed to deserialize Package object", e); throw e; } } private Package deserializeInternal(InputStream in) throws PackageDeserializationException, IOException, InterruptedException { // Read the initial Package message so we have the data to initialize the builder. We will read // the Targets in individually later. Build.Package packagePb = Build.Package.parseDelimitedFrom(in); Package.Builder builder; try { builder = new Package.Builder( PackageIdentifier.create( packagePb.getRepository(), new PathFragment(packagePb.getName())), null); } catch (LabelSyntaxException e) { throw new PackageDeserializationException(e); } StoredEventHandler eventHandler = new StoredEventHandler(); deserializeInternal(packagePb, eventHandler, builder, in); builder.addEvents(eventHandler.getEvents()); return builder.build(); } private static void deserializeEvent(StoredEventHandler eventHandler, Build.Event event) { String message = event.getMessage(); switch (event.getKind()) { case ERROR: eventHandler.handle(Event.error(message)); break; case WARNING: eventHandler.handle(Event.warn(message)); break; case INFO: eventHandler.handle(Event.info(message)); break; case PROGRESS: eventHandler.handle(Event.progress(message)); break; default: break; // Ignore } } private static List<?> deserializeGlobs(List<?> matches, Build.Attribute attrPb) { if (attrPb.getGlobCriteriaCount() == 0) { return matches; } Builder<GlobCriteria> criteriaBuilder = ImmutableList.builder(); for (Build.GlobCriteria criteriaPb : attrPb.getGlobCriteriaList()) { if (criteriaPb.hasGlob() && criteriaPb.getGlob()) { criteriaBuilder.add( GlobCriteria.fromGlobCall( ImmutableList.copyOf(criteriaPb.getIncludeList()), ImmutableList.copyOf(criteriaPb.getExcludeList()))); } else { criteriaBuilder.add( GlobCriteria.fromList(ImmutableList.copyOf(criteriaPb.getIncludeList()))); } } @SuppressWarnings({"unchecked", "rawtypes"}) GlobList<?> result = new GlobList(criteriaBuilder.build(), matches); return result; } // TODO(bazel-team): Verify that these put sane values in the attribute @VisibleForTesting static Object deserializeAttributeValue(Type<?> expectedType, Build.Attribute attrPb) throws PackageDeserializationException { switch (attrPb.getType()) { case INTEGER: return attrPb.hasIntValue() ? new Integer(attrPb.getIntValue()) : null; case STRING: if (!attrPb.hasStringValue()) { return null; } else if (expectedType == BuildType.NODEP_LABEL) { return deserializeLabel(attrPb.getStringValue()); } else { return attrPb.getStringValue(); } case LABEL: case OUTPUT: return attrPb.hasStringValue() ? deserializeLabel(attrPb.getStringValue()) : null; case STRING_LIST: if (expectedType == BuildType.NODEP_LABEL_LIST) { return deserializeGlobs(deserializeLabels(attrPb.getStringListValueList()), attrPb); } else { return deserializeGlobs(ImmutableList.copyOf(attrPb.getStringListValueList()), attrPb); } case LABEL_LIST: case OUTPUT_LIST: return deserializeGlobs(deserializeLabels(attrPb.getStringListValueList()), attrPb); case DISTRIBUTION_SET: return deserializeDistribs(attrPb.getStringListValueList()); case LICENSE: return attrPb.hasLicense() ? deserializeLicense(attrPb.getLicense()) : null; case STRING_DICT: { // Building an immutable map will fail if the builder was given duplicate keys. These // entry // lists may contain duplicate keys if the serialized map value was configured (e.g. via // the select function) and the different configuration values had keys in common. This is // because serialization flattens configurable map-valued attributes. // // As long as serialization does this flattening, to avoid failure during deserialization, // we dedupe entries in the list by their keys. // TODO(bazel-team): Serialize and deserialize configured values with fidelity (without // flattening them). ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); HashSet<String> keysSeenSoFar = Sets.newHashSet(); for (Build.StringDictEntry entry : attrPb.getStringDictValueList()) { String key = entry.getKey(); if (keysSeenSoFar.add(key)) { builder.put(key, entry.getValue()); } } return builder.build(); } case STRING_DICT_UNARY: { // See STRING_DICT case's comment about why this dedupes entries by their keys. ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); HashSet<String> keysSeenSoFar = Sets.newHashSet(); for (StringDictUnaryEntry entry : attrPb.getStringDictUnaryValueList()) { String key = entry.getKey(); if (keysSeenSoFar.add(key)) { builder.put(key, entry.getValue()); } } return builder.build(); } case FILESET_ENTRY_LIST: return deserializeFilesetEntries(attrPb.getFilesetListValueList()); case LABEL_LIST_DICT: { // See STRING_DICT case's comment about why this dedupes entries by their keys. ImmutableMap.Builder<String, List<Label>> builder = ImmutableMap.builder(); HashSet<String> keysSeenSoFar = Sets.newHashSet(); for (Build.LabelListDictEntry entry : attrPb.getLabelListDictValueList()) { String key = entry.getKey(); if (keysSeenSoFar.add(key)) { builder.put(key, deserializeLabels(entry.getValueList())); } } return builder.build(); } case STRING_LIST_DICT: { // See STRING_DICT case's comment about why this dedupes entries by their keys. ImmutableMap.Builder<String, List<String>> builder = ImmutableMap.builder(); HashSet<String> keysSeenSoFar = Sets.newHashSet(); for (Build.StringListDictEntry entry : attrPb.getStringListDictValueList()) { String key = entry.getKey(); if (keysSeenSoFar.add(key)) { builder.put(key, ImmutableList.copyOf(entry.getValueList())); } } return builder.build(); } case BOOLEAN: return attrPb.hasBooleanValue() ? attrPb.getBooleanValue() : null; case TRISTATE: return attrPb.hasStringValue() ? deserializeTriStateValue(attrPb.getStringValue()) : null; case INTEGER_LIST: return ImmutableList.copyOf(attrPb.getIntListValueList()); default: throw new PackageDeserializationException("Invalid discriminator: " + attrPb.getType()); } } private static FilesetEntry.SymlinkBehavior pbToSymlinkBehavior( Build.FilesetEntry.SymlinkBehavior symlinkBehavior) { switch (symlinkBehavior) { case COPY: return FilesetEntry.SymlinkBehavior.COPY; case DEREFERENCE: return FilesetEntry.SymlinkBehavior.DEREFERENCE; default: throw new IllegalStateException(); } } /** * An special {@code AttributeContainer} implementation that does not keep the location and can * contain a hashcode of the target attributes. */ public static class AttributeContainerWithoutLocation extends AttributeContainer { @Nullable private final HashCode syntheticAttrHash; private AttributeContainerWithoutLocation( RuleClass ruleClass, @Nullable HashCode syntheticAttrHash) { super(ruleClass, null); this.syntheticAttrHash = syntheticAttrHash; } @Override public Location getAttributeLocation(String attrName) { return EmptyLocation.INSTANCE; } @Override void setAttributeLocation(int attrIndex, Location location) { throw new UnsupportedOperationException("Setting location not supported"); } @Override void setAttributeLocation(Attribute attribute, Location location) { throw new UnsupportedOperationException("Setting location not supported"); } @Nullable public HashCode getSyntheticAttrHash() { return syntheticAttrHash; } private void clearIfNotLabel(String attr) { setAttributeValueByName(attr, null); } } /** * Creates a rule with the attribute values that are already parsed. * * <p><b>WARNING:</b> This assumes that the attribute values here have the right type and bypasses * some sanity checks. If they are of the wrong type, everything will come down burning. */ @SuppressWarnings("unchecked") private static Rule createRuleWithParsedAttributeValues( RuleClass ruleClass, Label label, Package.Builder pkgBuilder, Location ruleLocation, Map<String, ParsedAttributeValue> attributeValues, EventHandler eventHandler, AttributeContainer attributeContainer) throws LabelSyntaxException, InterruptedException { Rule rule = pkgBuilder.newRuleWithLabelAndAttrContainer( label, ruleClass, null, ruleLocation, attributeContainer); rule.checkValidityPredicate(eventHandler); for (Attribute attribute : rule.getRuleClassObject().getAttributes()) { ParsedAttributeValue value = attributeValues.get(attribute.getName()); if (attribute.isMandatory()) { Preconditions.checkState(value != null); } if (value == null) { continue; } rule.setAttributeValue(attribute, value.value, value.explicitlySpecified); ruleClass.checkAllowedValues(rule, attribute, eventHandler); if (attribute.getName().equals("visibility")) { // TODO(bazel-team): Verify that this cast works rule.setVisibility(PackageFactory.getVisibility((List<Label>) value.value)); } } rule.populateOutputFiles(eventHandler, pkgBuilder); Preconditions.checkState(!rule.containsErrors()); return rule; } private static class ParsedAttributeValue { private final boolean explicitlySpecified; private final Object value; private ParsedAttributeValue(boolean explicitlySpecified, Object value) { this.explicitlySpecified = explicitlySpecified; this.value = value; } } }
@ManageLifecycle public class SingleServerInventoryView extends ServerInventoryView<DataSegment> implements FilteredServerInventoryView { private static final EmittingLogger log = new EmittingLogger(SingleServerInventoryView.class); private static final Interner<DataSegment> DATA_SEGMENT_INTERNER = Interners.newWeakInterner(); private final ConcurrentMap<SegmentCallback, Predicate<Pair<DruidServerMetadata, DataSegment>>> segmentPredicates = new MapMaker().makeMap(); private final Predicate<Pair<DruidServerMetadata, DataSegment>> defaultFilter; @Inject public SingleServerInventoryView( final ZkPathsConfig zkPaths, final CuratorFramework curator, final ObjectMapper jsonMapper, final Predicate<Pair<DruidServerMetadata, DataSegment>> defaultFilter) { super( log, zkPaths.getAnnouncementsPath(), zkPaths.getServedSegmentsPath(), curator, jsonMapper, new TypeReference<DataSegment>() {}); Preconditions.checkNotNull(defaultFilter); this.defaultFilter = defaultFilter; } @Override protected DruidServer addInnerInventory( DruidServer container, String inventoryKey, DataSegment inventory) { Predicate<Pair<DruidServerMetadata, DataSegment>> predicate = Predicates.or(defaultFilter, Predicates.or(segmentPredicates.values())); if (predicate.apply(Pair.of(container.getMetadata(), inventory))) { addSingleInventory(container, inventory); } return container; } @Override protected DruidServer updateInnerInventory( DruidServer container, String inventoryKey, DataSegment inventory) { return addInnerInventory(container, inventoryKey, inventory); } @Override protected DruidServer removeInnerInventory(DruidServer container, String inventoryKey) { removeSingleInventory(container, inventoryKey); return container; } public void registerSegmentCallback( final Executor exec, final SegmentCallback callback, final Predicate<Pair<DruidServerMetadata, DataSegment>> filter) { SegmentCallback filteringCallback = new SingleServerInventoryView.FilteringSegmentCallback(callback, filter); segmentPredicates.put(filteringCallback, filter); registerSegmentCallback(exec, filteringCallback); } @Override protected void segmentCallbackRemoved(SegmentCallback callback) { segmentPredicates.remove(callback); } static class FilteringSegmentCallback implements SegmentCallback { private final SegmentCallback callback; private final Predicate<Pair<DruidServerMetadata, DataSegment>> filter; FilteringSegmentCallback( SegmentCallback callback, Predicate<Pair<DruidServerMetadata, DataSegment>> filter) { this.callback = callback; this.filter = filter; } @Override public CallbackAction segmentAdded(DruidServerMetadata server, DataSegment segment) { final CallbackAction action; if (filter.apply(Pair.of(server, segment))) { action = callback.segmentAdded(server, segment); } else { action = CallbackAction.CONTINUE; } return action; } @Override public CallbackAction segmentRemoved(DruidServerMetadata server, DataSegment segment) { final CallbackAction action; if (filter.apply(Pair.of(server, segment))) { action = callback.segmentRemoved(server, segment); } else { action = CallbackAction.CONTINUE; } return action; } @Override public CallbackAction segmentViewInitialized() { return callback.segmentViewInitialized(); } } @Override protected DataSegment internInventory(DataSegment sample) { return DATA_SEGMENT_INTERNER.intern(sample); } }
/** * The class {@code StringUtilities} defines utility methods for strings. * * @coverage dart.engine.utilities */ public final class StringUtilities { /** The empty String {@code ""}. */ public static final String EMPTY = ""; /** An empty array of strings. */ public static final String[] EMPTY_ARRAY = new String[0]; /** The {@link Interner} instance to use for {@link #intern(String)}. */ private static final Interner<String> INTERNER = Interners.newWeakInterner(); /** Abbreviates a String using ellipses inserted at left. */ public static String abbreviateLeft(String s, int width) { int length = s.length(); if (length > width) { if (width < 4) { throw new IllegalArgumentException("Minimal width is 4"); } return "..." + s.substring(length - (width - 3)); } return s; } /** * Return {@code true} if the three-character substring occurs at the end of the given string. * * @param string the string being searched * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @return {@code true} if the substring occurs at the end of the string */ public static boolean endsWith3(String string, int char1, int char2, int char3) { int length = string.length(); return length >= 3 && string.charAt(length - 3) == char1 && string.charAt(length - 2) == char2 && string.charAt(length - 1) == char3; } /** * Return {@code true} if the given string ends with the given character. * * @param string the string being searched * @param character the character being tested for * @return {@code true} if the string ends with the character */ public static boolean endsWithChar(String string, int character) { int length = string.length(); return length > 0 && string.charAt(length - 1) == character; } /** * Return the index of the first occurrence of the given character in the given string that is at * or after the given starting index. Return {@code -1} if the substring does not occur. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @return the index of the first occurrence of the substring */ public static int indexOf1(String string, int startIndex, int char1) { int index = startIndex; int last = string.length(); while (index < last) { if (string.charAt(index) == char1) { return index; } index++; } return -1; } /** * Return the index of the first occurrence of the given characters as a substring of the given * string that is at or after the given starting index. Return {@code -1} if the substring does * not occur. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @return the index of the first occurrence of the substring */ public static int indexOf2(String string, int startIndex, int char1, int char2) { int index = startIndex; int last = string.length() - 1; while (index < last) { if (string.charAt(index) == char1 && string.charAt(index + 1) == char2) { return index; } index++; } return -1; } /** * Return the index of the first occurrence of the given characters as a substring of the given * string that is at or after the given starting index. Return {@code -1} if the substring does * not occur. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @param char4 the fourth character in the substring * @return the index of the first occurrence of the substring */ public static int indexOf4( String string, int startIndex, int char1, int char2, int char3, int char4) { int index = startIndex; int last = string.length() - 3; while (index < last) { if (string.charAt(index) == char1 && string.charAt(index + 1) == char2 && string.charAt(index + 2) == char3 && string.charAt(index + 3) == char4) { return index; } index++; } return -1; } /** * Return the index of the first occurrence of the given characters as a substring of the given * string that is at or after the given starting index. Return {@code -1} if the substring does * not occur. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @param char4 the fourth character in the substring * @param char5 the fifth character in the substring * @return the index of the first occurrence of the substring */ public static int indexOf5( String string, int startIndex, int char1, int char2, int char3, int char4, int char5) { int index = startIndex; int last = string.length() - 4; while (index < last) { if (string.charAt(index) == char1 && string.charAt(index + 1) == char2 && string.charAt(index + 2) == char3 && string.charAt(index + 3) == char4 && string.charAt(index + 4) == char5) { return index; } index++; } return -1; } /** * Return the index of the first not letter/digit character in the given string that is at or * after the given starting index. Return the length of the given string if the all characters to * the end are letters/digits. * * @param string the string being searched * @param startIndex the index at which the search should begin * @return the index of the first not letter/digit character */ public static int indexOfFirstNotLetterDigit(String string, int startIndex) { int index = startIndex; int last = string.length(); while (index < last) { char c = string.charAt(index); if (!Character.isLetterOrDigit(c)) { return index; } index++; } return last; } /** * Returns a canonical representation for the given {@link String}. * * @return the given {@link String} or its canonical representation. */ public static String intern(String str) { if (str == null) { return null; } str = new String(str); return INTERNER.intern(str); } /** * Checks if the CharSequence contains only Unicode letters. * * <p>{@code null} will return {@code false}. An empty CharSequence (length()=0) will return * {@code false}. * * <pre> * StringUtils.isAlpha(null) = false * StringUtils.isAlpha("") = false * StringUtils.isAlpha(" ") = false * StringUtils.isAlpha("abc") = true * StringUtils.isAlpha("ab2c") = false * StringUtils.isAlpha("ab-c") = false * </pre> * * @param cs the CharSequence to check, may be null * @return {@code true} if only contains letters, and is non-null */ public static boolean isAlpha(CharSequence cs) { if (cs == null || cs.length() == 0) { return false; } int sz = cs.length(); for (int i = 0; i < sz; i++) { if (Character.isLetter(cs.charAt(i)) == false) { return false; } } return true; } /** * Return {@code true} if the given CharSequence is empty ("") or null. * * <pre> * StringUtils.isEmpty(null) = true * StringUtils.isEmpty("") = true * StringUtils.isEmpty(" ") = false * StringUtils.isEmpty("bob") = false * StringUtils.isEmpty(" bob ") = false * </pre> * * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is empty or null */ public static boolean isEmpty(CharSequence cs) { return cs == null || cs.length() == 0; } /** * Checks if the String can be used as a tag name. * * <p>{@code null} will return {@code false}. An empty String (length()=0) will return {@code * false}. * * <pre> * StringUtils.isAlpha(null) = false * StringUtils.isAlpha("") = false * StringUtils.isAlpha(" ") = false * StringUtils.isAlpha("ab c") = false * StringUtils.isAlpha("abc") = true * StringUtils.isAlpha("ab2c") = true * StringUtils.isAlpha("ab-c") = true * </pre> * * @param s the String to check, may be null * @return {@code true} if can be used as a tag name, and is non-null */ public static boolean isTagName(String s) { if (s == null || s.length() == 0) { return false; } int sz = s.length(); for (int i = 0; i < sz; i++) { char c = s.charAt(i); if (!Character.isLetter(c)) { if (i == 0) { return false; } if (!Character.isDigit(c) && c != '-') { return false; } } } return true; } /** * Produce a string containing all of the names in the given array, surrounded by single quotes, * and separated by commas. The list must contain at least two elements. * * @param names the names to be printed * @return the result of printing the names */ public static String printListOfQuotedNames(String[] names) { if (names == null) { throw new IllegalArgumentException("The list must not be null"); } int count = names.length; if (count < 2) { throw new IllegalArgumentException("The list must contain at least two names"); } StringBuilder builder = new StringBuilder(); builder.append("'"); builder.append(names[0]); builder.append("'"); for (int i = 1; i < count - 1; i++) { builder.append(", '"); builder.append(names[i]); builder.append("'"); } builder.append(" and '"); builder.append(names[count - 1]); builder.append("'"); return builder.toString(); } /** * Return {@code true} if the two-character substring occurs at the given index in the given * string. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @return {@code true} if the substring occurs at the given index in the string */ public static boolean startsWith2(String string, int startIndex, int char1, int char2) { return string.length() - startIndex >= 2 && string.charAt(startIndex) == char1 && string.charAt(startIndex + 1) == char2; } /** * Return {@code true} if the three-character substring occurs at the given index in the given * string. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @return {@code true} if the substring occurs at the given index in the string */ public static boolean startsWith3( String string, int startIndex, int char1, int char2, int char3) { return string.length() - startIndex >= 3 && string.charAt(startIndex) == char1 && string.charAt(startIndex + 1) == char2 && string.charAt(startIndex + 2) == char3; } /** * Return {@code true} if the four-character substring occurs at the given index in the given * string. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @param char4 the fourth character in the substring * @return {@code true} if the substring occurs at the given index in the string */ public static boolean startsWith4( String string, int startIndex, int char1, int char2, int char3, int char4) { return string.length() - startIndex >= 4 && string.charAt(startIndex) == char1 && string.charAt(startIndex + 1) == char2 && string.charAt(startIndex + 2) == char3 && string.charAt(startIndex + 3) == char4; } /** * Return {@code true} if the five-character substring occurs at the given index in the given * string. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @param char4 the fourth character in the substring * @param char5 the fifth character in the substring * @return {@code true} if the substring occurs at the given index in the string */ public static boolean startsWith5( String string, int startIndex, int char1, int char2, int char3, int char4, int char5) { return string.length() - startIndex >= 5 && string.charAt(startIndex) == char1 && string.charAt(startIndex + 1) == char2 && string.charAt(startIndex + 2) == char3 && string.charAt(startIndex + 3) == char4 && string.charAt(startIndex + 4) == char5; } /** * Return {@code true} if the six-character substring occurs at the given index in the given * string. * * @param string the string being searched * @param startIndex the index at which the search should begin * @param char1 the first character in the substring * @param char2 the second character in the substring * @param char3 the third character in the substring * @param char4 the fourth character in the substring * @param char5 the fifth character in the substring * @param char6 the sixth character in the substring * @return {@code true} if the substring occurs at the given index in the string */ public static boolean startsWith6( String string, int startIndex, int char1, int char2, int char3, int char4, int char5, int char6) { return string.length() - startIndex >= 6 && string.charAt(startIndex) == char1 && string.charAt(startIndex + 1) == char2 && string.charAt(startIndex + 2) == char3 && string.charAt(startIndex + 3) == char4 && string.charAt(startIndex + 4) == char5 && string.charAt(startIndex + 5) == char6; } /** * Return {@code true} if the given string starts with the given character. * * @param string the string being searched * @param character the character being tested for * @return {@code true} if the string starts with the character */ public static boolean startsWithChar(String string, int character) { return string.length() > 0 && string.charAt(0) == character; } /** * Gets the substring after the first occurrence of a separator. The separator is not returned. * * <p>A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. A {@code null} separator will return the empty string if the input string is * not {@code null}. * * <p>If nothing is found, the empty string is returned. * * <pre> * StringUtils.substringAfter(null, *) = null * StringUtils.substringAfter("", *) = "" * StringUtils.substringAfter(*, null) = "" * StringUtils.substringAfter("abc", "a") = "bc" * StringUtils.substringAfter("abcba", "b") = "cba" * StringUtils.substringAfter("abc", "c") = "" * StringUtils.substringAfter("abc", "d") = "" * StringUtils.substringAfter("abc", "") = "abc" * </pre> * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring after the first occurrence of the separator, {@code null} if null String * input */ public static String substringAfter(String str, String separator) { if (isEmpty(str)) { return str; } if (separator == null) { return EMPTY; } int pos = str.indexOf(separator); if (pos == -1) { return EMPTY; } return str.substring(pos + separator.length()); } /** * Return the substring before the first occurrence of a separator. The separator is not returned. * * <p>A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. A {@code null} separator will return the input string. * * <p>If nothing is found, the string input is returned. * * <pre> * StringUtils.substringBefore(null, *) = null * StringUtils.substringBefore("", *) = "" * StringUtils.substringBefore("abc", "a") = "" * StringUtils.substringBefore("abcba", "b") = "a" * StringUtils.substringBefore("abc", "c") = "ab" * StringUtils.substringBefore("abc", "d") = "abc" * StringUtils.substringBefore("abc", "") = "" * StringUtils.substringBefore("abc", null) = "abc" * </pre> * * @param str the string to get a substring from, may be null * @param separator the string to search for, may be null * @return the substring before the first occurrence of the separator */ public static String substringBefore(String str, String separator) { if (isEmpty(str) || separator == null) { return str; } if (separator.length() == 0) { return EMPTY; } int pos = str.indexOf(separator); if (pos == -1) { return str; } return str.substring(0, pos); } /** * Return the substring before the first occurrence of a separator. The separator is not included * in the returned value. * * <p>A {@code null} string input will return {@code null}. An empty ("") string input will return * the empty string. * * <p>If nothing is found, the string input is returned. * * <pre> * StringUtils.substringBefore(null, *) = null * StringUtils.substringBefore("", *) = "" * StringUtils.substringBefore("abc", 'a') = "" * StringUtils.substringBefore("abcba", 'b') = "a" * StringUtils.substringBefore("abc", 'c') = "ab" * StringUtils.substringBefore("abc", 'd') = "abc" * </pre> * * @param str the string to get a substring from, may be null * @param separator the character to search for * @return the substring before the first occurrence of the separator */ public static String substringBeforeChar(String str, int separator) { if (isEmpty(str)) { return str; } int pos = str.indexOf(separator); if (pos < 0) { return str; } return str.substring(0, pos); } /** Prevent the creation of instances of this class. */ private StringUtilities() {} }