1   /*
2    * Copyright 2018 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.linecorp.centraldogma.server.internal.storage.repository.cache;
18  
19  import static com.google.common.base.Preconditions.checkState;
20  import static com.linecorp.centraldogma.internal.Util.validateJsonFilePath;
21  import static java.util.Objects.requireNonNull;
22  
23  import java.util.List;
24  import java.util.Objects;
25  import java.util.concurrent.CompletableFuture;
26  
27  import javax.annotation.Nullable;
28  
29  import com.google.common.base.MoreObjects.ToStringHelper;
30  
31  import com.linecorp.centraldogma.common.MergeQuery;
32  import com.linecorp.centraldogma.common.MergeSource;
33  import com.linecorp.centraldogma.common.MergedEntry;
34  import com.linecorp.centraldogma.common.Revision;
35  import com.linecorp.centraldogma.server.internal.storage.repository.CacheableCall;
36  import com.linecorp.centraldogma.server.storage.repository.Repository;
37  
38  final class CacheableMergeQueryCall extends CacheableCall<MergedEntry<?>> {
39  
40      private final Revision revision;
41      private final MergeQuery<?> query;
42      private final int hashCode;
43  
44      @Nullable
45      MergedEntry<?> computedValue;
46  
47      CacheableMergeQueryCall(Repository repo, Revision revision, MergeQuery<?> query) {
48          super(repo);
49          this.revision = requireNonNull(revision, "revision");
50          this.query = requireNonNull(query, "query");
51          // Only JSON files can currently be merged.
52          query.mergeSources().forEach(path -> validateJsonFilePath(path.path(), "path"));
53  
54          hashCode = Objects.hash(revision, query) * 31 + System.identityHashCode(repo);
55  
56          assert !revision.isRelative();
57      }
58  
59      @Override
60      protected int weigh(MergedEntry<?> value) {
61          int weight = 0;
62          final List<MergeSource> mergeSources = query.mergeSources();
63          weight += mergeSources.size();
64          for (MergeSource mergeSource : mergeSources) {
65              weight += mergeSource.path().length();
66          }
67          final List<String> expressions = query.expressions();
68          weight += expressions.size();
69          for (String expression : expressions) {
70              weight += expression.length();
71          }
72          if (value != null) {
73              weight += value.contentAsText().length();
74          }
75          return weight;
76      }
77  
78      @Override
79      public CompletableFuture<MergedEntry<?>> execute() {
80          checkState(computedValue != null, "computedValue is not set yet.");
81          return CompletableFuture.completedFuture(computedValue);
82      }
83  
84      void computedValue(MergedEntry<?> computedValue) {
85          checkState(this.computedValue == null, "computedValue is already set.");
86          this.computedValue = requireNonNull(computedValue, "computedValue");
87      }
88  
89      @Override
90      public int hashCode() {
91          return hashCode;
92      }
93  
94      @Override
95      public boolean equals(Object o) {
96          if (!super.equals(o)) {
97              return false;
98          }
99  
100         final CacheableMergeQueryCall that = (CacheableMergeQueryCall) o;
101         return revision.equals(that.revision) &&
102                query.equals(that.query);
103     }
104 
105     @Override
106     protected void toString(ToStringHelper helper) {
107         helper.add("revision", revision)
108               .add("query", query);
109     }
110 }