Skip to content

Commit

Permalink
serialization registration from inside features
Browse files Browse the repository at this point in the history
  • Loading branch information
kkriske committed Dec 20, 2020
1 parent b773acb commit 0128724
Show file tree
Hide file tree
Showing 14 changed files with 492 additions and 117 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.nativeimage.hosted;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;

@Platforms(Platform.HOSTED_ONLY.class)
public final class RuntimeSerialization {

public static void register(Class<?>... classes) {
ImageSingletons.lookup(RuntimeSerializationSupport.class).register(classes);
}

private RuntimeSerialization() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.graalvm.nativeimage.impl;

import java.util.Collection;
import java.util.Collections;

public interface RuntimeSerializationSupport {

default void register(Class<?>... classes) {
for (Class<?> clazz : classes) {
register(clazz, Collections.emptyList());
}
}

void register(Class<?> clazz, Collection<String> checkSums);

}
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public ResourceConfiguration loadResourceConfig(Function<IOException, Exception>

public SerializationConfiguration loadSerializationConfig(Function<IOException, Exception> exceptionHandler) throws Exception {
SerializationConfiguration serializationConfiguration = new SerializationConfiguration();
loadConfig(serializationConfigPaths, new SerializationConfigurationParser((targetSerializationClass, checksums) -> serializationConfiguration.addAll(targetSerializationClass, checksums)),
loadConfig(serializationConfigPaths, new SerializationConfigurationParser<>(new SerializationConfiguration.ParseAdapter(serializationConfiguration)),
exceptionHandler);
return serializationConfiguration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,60 +26,88 @@
package com.oracle.svm.configure.config;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import com.oracle.svm.configure.json.JsonPrintable;
import com.oracle.svm.configure.json.JsonWriter;
import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.configure.SerializationConfigurationParserDelegate;
import com.oracle.svm.core.util.UserError;
import jdk.vm.ci.meta.JavaKind;

public class SerializationConfiguration implements JsonPrintable {

private final ConcurrentHashMap<String, Set<String>> serializations = new ConcurrentHashMap<>();
public static class ParseAdapter implements SerializationConfigurationParserDelegate<SerializationConfigurationType> {

public void addAll(String serializationTargetClass, Collection<String> checksums) {
serializations.computeIfAbsent(serializationTargetClass, key -> new LinkedHashSet<>()).addAll(checksums);
}
private final SerializationConfiguration configuration;

public ParseAdapter(SerializationConfiguration configuration) {
this.configuration = configuration;
}

@Override
public void registerType(SerializationConfigurationType type, List<String> checkSums) {
configuration.add(type, checkSums);
}

public void add(String serializationTargetClass, String checksum) {
if (checksum == null) {
addAll(serializationTargetClass, Collections.emptySet());
} else {
addAll(serializationTargetClass, Collections.singleton(checksum));
@Override
public TypeResult<SerializationConfigurationType> resolveTypeResult(String typeName) {
SerializationConfigurationType type = configuration.get(typeName);
SerializationConfigurationType result = type != null ? type : new SerializationConfigurationType(typeName);
return TypeResult.forType(typeName, result);
}
}

public boolean contains(String serializationTargetClass, String checksum) {
Set<String> checksums = serializations.get(serializationTargetClass);
return checksums != null && checksums.contains(checksum);
private final ConcurrentHashMap<String, SerializationConfigurationType> serializations = new ConcurrentHashMap<>();

public SerializationConfigurationType get(String qualifiedJavaName) {
return serializations.get(qualifiedJavaName);
}

public void add(SerializationConfigurationType type, List<String> checksums) {
SerializationConfigurationType previous = serializations.putIfAbsent(type.getQualifiedJavaName(), type);
UserError.guarantee(previous == null || previous == type, "Cannot replace existing type %s with %s", previous, type);
type.addCheckSums(checksums);
}

public SerializationConfigurationType getOrCreateType(String qualifiedForNameString) {
assert qualifiedForNameString.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert !qualifiedForNameString.endsWith("[]") : "Requires Class.forName syntax, for example '[Ljava.lang.String;'";

String s = qualifiedForNameString;
int n = 0;
while (n < s.length() && s.charAt(n) == '[') {
n++;
}
if (n > 0) { // transform to Java source syntax
StringBuilder sb = new StringBuilder(s.length() + n);
if (s.charAt(n) == 'L' && s.charAt(s.length() - 1) == ';') {
sb.append(s, n + 1, s.length() - 1); // cut off leading '[' and 'L' and trailing ';'
} else if (n == s.length() - 1) {
sb.append(JavaKind.fromPrimitiveOrVoidTypeChar(s.charAt(n)).getJavaName());
} else {
throw new IllegalArgumentException();
}
for (int i = 0; i < n; i++) {
sb.append("[]");
}
s = sb.toString();
}
return serializations.computeIfAbsent(s, SerializationConfigurationType::new);
}

@Override
public void printJson(JsonWriter writer) throws IOException {
writer.append('[').indent();
String prefix = "";
for (Map.Entry<String, Set<String>> entry : serializations.entrySet()) {
writer.append(prefix);
writer.newline().append('{').newline();
String className = entry.getKey();
writer.quote("name").append(":").quote(className);
Set<String> checksums = entry.getValue();
if (!checksums.isEmpty()) {
writer.append(",").newline();
writer.quote("checksum").append(':');
if (checksums.size() == 1) {
writer.quote(checksums.iterator().next());
} else {
writer.append(checksums.stream()
.map(JsonWriter::quoteString)
.collect(Collectors.joining(", ", "[", "]")));
}
}
writer.newline().append('}');
List<SerializationConfigurationType> list = new ArrayList<>(serializations.values());
list.sort(Comparator.comparing(SerializationConfigurationType::getQualifiedJavaName));
for (SerializationConfigurationType type : list) {
writer.append(prefix).newline();
type.printJson(writer);
prefix = ",";
}
writer.unindent().newline();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.configure.config;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

import com.oracle.svm.configure.json.JsonPrintable;
import com.oracle.svm.configure.json.JsonWriter;

public class SerializationConfigurationType implements JsonPrintable {

private final String qualifiedJavaName;
private Set<String> checkSums;

public SerializationConfigurationType(String qualifiedJavaName) {
assert qualifiedJavaName.indexOf('/') == -1 : "Requires qualified Java name, not internal representation";
assert !qualifiedJavaName.startsWith("[") : "Requires Java source array syntax, for example java.lang.String[]";
this.qualifiedJavaName = qualifiedJavaName;
}

public String getQualifiedJavaName() {
return qualifiedJavaName;
}

public void addCheckSums(Collection<String> checkSumsToAdd) {
if (checkSums == null) {
checkSums = new HashSet<>();
}
checkSums.addAll(checkSumsToAdd);
}

public void addCheckSum(String checkSum) {
addCheckSums(Collections.singleton(checkSum));
}

@Override
public void printJson(JsonWriter writer) throws IOException {
writer.append('{').indent().newline();
writer.quote("name").append(':').quote(qualifiedJavaName);
if (checkSums != null && !checkSums.isEmpty()) {
writer.append(',').newline();
writer.quote("checksum").append(':');
if (checkSums.size() == 1) {
writer.quote(checkSums.iterator().next());
} else {
writer.append(checkSums.stream()
.map(JsonWriter::quoteString)
.collect(Collectors.joining(", ", "[", "]")));
}
}
writer.unindent().newline().append('}');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ void processEntry(Map<String, ?> entry) {
List<?> args = (List<?>) entry.get("args");
if ("ObjectStreamClass.<init>".equals(function)) {
expectSize(args, 2);
serializationConfiguration.add((String) args.get(0), (String) args.get(1));
serializationConfiguration.getOrCreateType((String) args.get(0)).addCheckSum((String) args.get(1));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,15 @@
import java.util.List;
import java.util.Map;

import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.util.json.JSONParser;
import com.oracle.svm.core.util.json.JSONParserException;

public class SerializationConfigurationParser extends ConfigurationParser {
private final SerializationParserFunction consumer;
public class SerializationConfigurationParser<T> extends ConfigurationParser {
private final SerializationConfigurationParserDelegate<T> delegate;

public SerializationConfigurationParser(SerializationParserFunction consumer) {
this.consumer = consumer;
public SerializationConfigurationParser(SerializationConfigurationParserDelegate<T> delegate) {
this.delegate = delegate;
}

@Override
Expand All @@ -48,7 +49,7 @@ public void parseAndRegister(Reader reader) throws IOException {
Object json = parser.parse();
for (Object serializationKey : asList(json, "first level of document must be an array of serialization lists")) {
Map<String, Object> data = asMap(serializationKey, "second level of document must be serialization descriptor objects ");
String targetSerializationClass = asString(data.get("name"));
String className = asString(data.get("name"));
Object checksumValue = data.get("checksum");
List<String> checksums = new ArrayList<>();
if (checksumValue != null) {
Expand All @@ -62,12 +63,18 @@ public void parseAndRegister(Reader reader) throws IOException {
checksums.add(asString(jsonChecksum, "checksum"));
}
}
consumer.accept(targetSerializationClass, checksums);
TypeResult<T> result = delegate.resolveTypeResult(className);
if (result.isPresent()) {
delegate.registerType(result.get(), checksums);
} else {
handleUnresolvedClass(className);
}
}
}

@FunctionalInterface
public interface SerializationParserFunction {
void accept(String targetSerializationClass, List<String> checksum);
protected void handleUnresolvedClass(String className) {
throw new JSONParserException("Could not resolve " + className + " during serialization configuration." +
" The missing of this class can't be ignored even if -H:+AllowIncompleteClasspath is set." +
" Please make sure it is in the classpath");
}
}
Loading

0 comments on commit 0128724

Please sign in to comment.