-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add weak cache of built parsers across MessageMarshaller instances (#39)
- Loading branch information
1 parent
ba98f5e
commit 10ef326
Showing
4 changed files
with
234 additions
and
80 deletions.
There are no files selected for viewing
68 changes: 68 additions & 0 deletions
68
src/main/java/org/curioswitch/common/protobuf/json/MarshallerCache.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright (c) Choko ([email protected]) | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
package org.curioswitch.common.protobuf.json; | ||
|
||
import java.lang.ref.ReferenceQueue; | ||
import java.lang.ref.WeakReference; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.locks.ReentrantLock; | ||
import javax.annotation.Nullable; | ||
|
||
// Because it is fairly expensive to build a TypeSpecificMarshaller, we go ahead | ||
// and cache to save time across different MessageMarshaller instances. We still | ||
// want to make sure they can be garbage collected, so we use weak references. | ||
final class MarshallerCache { | ||
|
||
private final Map<MarshallerOptions, MarshallerReference> cache = new HashMap<>(); | ||
private final ReferenceQueue<TypeSpecificMarshaller<?>> queue = new ReferenceQueue<>(); | ||
private final ReentrantLock lock = new ReentrantLock(); | ||
|
||
@Nullable | ||
TypeSpecificMarshaller<?> get(MarshallerOptions key) { | ||
lock.lock(); | ||
try { | ||
clean(); | ||
MarshallerReference ref = cache.get(key); | ||
return ref != null ? ref.get() : null; | ||
} finally { | ||
lock.unlock(); | ||
} | ||
} | ||
|
||
void put(MarshallerOptions key, TypeSpecificMarshaller<?> value) { | ||
lock.lock(); | ||
try { | ||
clean(); | ||
cache.put(key, new MarshallerReference(key, value, queue)); | ||
} finally { | ||
lock.unlock(); | ||
} | ||
} | ||
|
||
private void clean() { | ||
MarshallerReference ref; | ||
while ((ref = (MarshallerReference) queue.poll()) != null) { | ||
cache.remove(ref.getKey()); | ||
} | ||
} | ||
|
||
private static class MarshallerReference extends WeakReference<TypeSpecificMarshaller<?>> { | ||
private final MarshallerOptions key; | ||
|
||
MarshallerReference( | ||
MarshallerOptions key, | ||
TypeSpecificMarshaller<?> value, | ||
ReferenceQueue<TypeSpecificMarshaller<?>> queue) { | ||
super(value, queue); | ||
this.key = key; | ||
} | ||
|
||
MarshallerOptions getKey() { | ||
return key; | ||
} | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
src/main/java/org/curioswitch/common/protobuf/json/MarshallerOptions.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* Copyright (c) Choko ([email protected]) | ||
* SPDX-License-Identifier: MIT | ||
*/ | ||
|
||
package org.curioswitch.common.protobuf.json; | ||
|
||
import com.google.protobuf.Descriptors.FieldDescriptor; | ||
import com.google.protobuf.Message; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
|
||
class MarshallerOptions { | ||
|
||
private final Message prototype; | ||
private final boolean includingDefaultValueFields; | ||
private final Set<FieldDescriptor> fieldsToAlwaysOutput; | ||
private final boolean preservingProtoFieldNames; | ||
private final boolean ignoringUnknownFields; | ||
private final boolean printingEnumsAsInts; | ||
private final boolean sortingMapKeys; | ||
|
||
MarshallerOptions( | ||
Message prototype, | ||
boolean includingDefaultValueFields, | ||
Set<FieldDescriptor> fieldsToAlwaysOutput, | ||
boolean preservingProtoFieldNames, | ||
boolean ignoringUnknownFields, | ||
boolean printingEnumsAsInts, | ||
boolean sortingMapKeys) { | ||
this.prototype = prototype; | ||
this.includingDefaultValueFields = includingDefaultValueFields; | ||
this.fieldsToAlwaysOutput = fieldsToAlwaysOutput; | ||
this.preservingProtoFieldNames = preservingProtoFieldNames; | ||
this.ignoringUnknownFields = ignoringUnknownFields; | ||
this.printingEnumsAsInts = printingEnumsAsInts; | ||
this.sortingMapKeys = sortingMapKeys; | ||
} | ||
|
||
Message getPrototype() { | ||
return prototype; | ||
} | ||
|
||
public boolean isIncludingDefaultValueFields() { | ||
return includingDefaultValueFields; | ||
} | ||
|
||
Set<FieldDescriptor> getFieldsToAlwaysOutput() { | ||
return fieldsToAlwaysOutput; | ||
} | ||
|
||
boolean isPreservingProtoFieldNames() { | ||
return preservingProtoFieldNames; | ||
} | ||
|
||
boolean isIgnoringUnknownFields() { | ||
return ignoringUnknownFields; | ||
} | ||
|
||
boolean isPrintingEnumsAsInts() { | ||
return printingEnumsAsInts; | ||
} | ||
|
||
boolean isSortingMapKeys() { | ||
return sortingMapKeys; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (!(o instanceof MarshallerOptions)) { | ||
return false; | ||
} | ||
MarshallerOptions that = (MarshallerOptions) o; | ||
return includingDefaultValueFields == that.includingDefaultValueFields | ||
&& preservingProtoFieldNames == that.preservingProtoFieldNames | ||
&& ignoringUnknownFields == that.ignoringUnknownFields | ||
&& printingEnumsAsInts == that.printingEnumsAsInts | ||
&& sortingMapKeys == that.sortingMapKeys | ||
&& prototype.getDescriptorForType().equals(that.prototype.getDescriptorForType()) | ||
&& fieldsToAlwaysOutput.equals(that.fieldsToAlwaysOutput); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash( | ||
prototype.getDescriptorForType(), | ||
includingDefaultValueFields, | ||
fieldsToAlwaysOutput, | ||
preservingProtoFieldNames, | ||
ignoringUnknownFields, | ||
printingEnumsAsInts, | ||
sortingMapKeys); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters