You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sb...@apache.org on 2015/08/25 09:05:53 UTC
[12/14] ignite git commit: ignite-1258: portable objects API support
in Ignite
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java
new file mode 100644
index 0000000..83dd01d
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableContext.java
@@ -0,0 +1,1089 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.*;
+import org.apache.ignite.internal.*;
+import org.apache.ignite.internal.processors.cache.portable.*;
+import org.apache.ignite.internal.util.*;
+import org.apache.ignite.internal.util.lang.*;
+import org.apache.ignite.internal.util.typedef.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.lang.*;
+import org.apache.ignite.marshaller.*;
+import org.apache.ignite.marshaller.optimized.*;
+import org.apache.ignite.marshaller.portable.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+import org.jsr166.*;
+
+import java.io.*;
+import java.math.*;
+import java.net.*;
+import java.sql.*;
+import java.util.*;
+import java.util.Date;
+import java.util.concurrent.*;
+import java.util.jar.*;
+
+/**
+ * Portable context.
+ */
+public class PortableContext implements Externalizable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ static final PortableIdMapper DFLT_ID_MAPPER = new IdMapperWrapper(null);
+
+ /** */
+ static final PortableIdMapper BASIC_CLS_ID_MAPPER = new BasicClassIdMapper();
+
+ /** */
+ static final char[] LOWER_CASE_CHARS;
+
+ /** */
+ static final char MAX_LOWER_CASE_CHAR = 0x7e;
+
+ /**
+ *
+ */
+ static {
+ LOWER_CASE_CHARS = new char[MAX_LOWER_CASE_CHAR + 1];
+
+ for (char c = 0; c <= MAX_LOWER_CASE_CHAR; c++)
+ LOWER_CASE_CHARS[c] = Character.toLowerCase(c);
+ }
+
+ /** */
+ private final ConcurrentMap<Integer, Collection<Integer>> metaDataCache = new ConcurrentHashMap8<>();
+
+ /** */
+ private final ConcurrentMap<Class<?>, PortableClassDescriptor> descByCls = new ConcurrentHashMap8<>();
+
+ /** */
+ private final ConcurrentMap<Integer, PortableClassDescriptor> userTypes = new ConcurrentHashMap8<>(0);
+
+ /** */
+ private final Map<Integer, PortableClassDescriptor> predefinedTypes = new HashMap<>();
+
+ /** */
+ private final Set<Class> predefinedClasses = new HashSet<>();
+
+ /** */
+ private final Map<Class<? extends Collection>, Byte> colTypes = new HashMap<>();
+
+ /** */
+ private final Map<Class<? extends Map>, Byte> mapTypes = new HashMap<>();
+
+ /** */
+ private final Map<Integer, PortableIdMapper> mappers = new ConcurrentHashMap8<>(0);
+
+ /** */
+ private final Map<String, PortableIdMapper> typeMappers = new ConcurrentHashMap8<>(0);
+
+ /** */
+ private Map<Integer, Boolean> metaEnabled = new HashMap<>(0);
+
+ /** */
+ private Set<Integer> usingTs = new HashSet<>();
+
+ /** */
+ private PortableMetaDataHandler metaHnd;
+
+ /** */
+ private MarshallerContext marshCtx;
+
+ /** */
+ private String gridName;
+
+ /** */
+ private PortableMarshaller marsh;
+
+ /** */
+ private final OptimizedMarshaller optmMarsh = new OptimizedMarshaller();
+
+ /**
+ * For {@link Externalizable}.
+ */
+ public PortableContext() {
+ // No-op.
+ }
+
+ /**
+ * @param metaHnd Meta data handler.
+ * @param gridName Grid name.
+ */
+ public PortableContext(PortableMetaDataHandler metaHnd, @Nullable String gridName) {
+ assert metaHnd != null;
+
+ this.metaHnd = metaHnd;
+ this.gridName = gridName;
+
+ colTypes.put(ArrayList.class, GridPortableMarshaller.ARR_LIST);
+ colTypes.put(LinkedList.class, GridPortableMarshaller.LINKED_LIST);
+ colTypes.put(HashSet.class, GridPortableMarshaller.HASH_SET);
+ colTypes.put(LinkedHashSet.class, GridPortableMarshaller.LINKED_HASH_SET);
+ colTypes.put(TreeSet.class, GridPortableMarshaller.TREE_SET);
+ colTypes.put(ConcurrentSkipListSet.class, GridPortableMarshaller.CONC_SKIP_LIST_SET);
+
+ mapTypes.put(HashMap.class, GridPortableMarshaller.HASH_MAP);
+ mapTypes.put(LinkedHashMap.class, GridPortableMarshaller.LINKED_HASH_MAP);
+ mapTypes.put(TreeMap.class, GridPortableMarshaller.TREE_MAP);
+ mapTypes.put(ConcurrentHashMap.class, GridPortableMarshaller.CONC_HASH_MAP);
+ mapTypes.put(ConcurrentHashMap8.class, GridPortableMarshaller.CONC_HASH_MAP);
+ mapTypes.put(Properties.class, GridPortableMarshaller.PROPERTIES_MAP);
+
+ registerPredefinedType(Byte.class, GridPortableMarshaller.BYTE);
+ registerPredefinedType(Boolean.class, GridPortableMarshaller.BOOLEAN);
+ registerPredefinedType(Short.class, GridPortableMarshaller.SHORT);
+ registerPredefinedType(Character.class, GridPortableMarshaller.CHAR);
+ registerPredefinedType(Integer.class, GridPortableMarshaller.INT);
+ registerPredefinedType(Long.class, GridPortableMarshaller.LONG);
+ registerPredefinedType(Float.class, GridPortableMarshaller.FLOAT);
+ registerPredefinedType(Double.class, GridPortableMarshaller.DOUBLE);
+ registerPredefinedType(String.class, GridPortableMarshaller.STRING);
+ registerPredefinedType(BigDecimal.class, GridPortableMarshaller.DECIMAL);
+ registerPredefinedType(Date.class, GridPortableMarshaller.DATE);
+ registerPredefinedType(UUID.class, GridPortableMarshaller.UUID);
+ // TODO: How to handle timestamp? It has the same ID in .Net.
+ registerPredefinedType(Timestamp.class, GridPortableMarshaller.DATE);
+
+ registerPredefinedType(byte[].class, GridPortableMarshaller.BYTE_ARR);
+ registerPredefinedType(short[].class, GridPortableMarshaller.SHORT_ARR);
+ registerPredefinedType(int[].class, GridPortableMarshaller.INT_ARR);
+ registerPredefinedType(long[].class, GridPortableMarshaller.LONG_ARR);
+ registerPredefinedType(float[].class, GridPortableMarshaller.FLOAT_ARR);
+ registerPredefinedType(double[].class, GridPortableMarshaller.DOUBLE_ARR);
+ registerPredefinedType(char[].class, GridPortableMarshaller.CHAR_ARR);
+ registerPredefinedType(boolean[].class, GridPortableMarshaller.BOOLEAN_ARR);
+ registerPredefinedType(BigDecimal[].class, GridPortableMarshaller.DECIMAL_ARR);
+ registerPredefinedType(String[].class, GridPortableMarshaller.STRING_ARR);
+ registerPredefinedType(UUID[].class, GridPortableMarshaller.UUID_ARR);
+ registerPredefinedType(Date[].class, GridPortableMarshaller.DATE_ARR);
+ registerPredefinedType(Object[].class, GridPortableMarshaller.OBJ_ARR);
+
+ registerPredefinedType(ArrayList.class, 0);
+ registerPredefinedType(LinkedList.class, 0);
+ registerPredefinedType(HashSet.class, 0);
+ registerPredefinedType(LinkedHashSet.class, 0);
+ registerPredefinedType(TreeSet.class, 0);
+ registerPredefinedType(ConcurrentSkipListSet.class, 0);
+
+ registerPredefinedType(HashMap.class, 0);
+ registerPredefinedType(LinkedHashMap.class, 0);
+ registerPredefinedType(TreeMap.class, 0);
+ registerPredefinedType(ConcurrentHashMap.class, 0);
+ registerPredefinedType(ConcurrentHashMap8.class, 0);
+
+ registerPredefinedType(GridMapEntry.class, 60);
+ registerPredefinedType(IgniteBiTuple.class, 61);
+ registerPredefinedType(T2.class, 62);
+
+ registerPredefinedType(PortableObjectImpl.class, 63);
+
+ registerPredefinedType(PortableMetaDataImpl.class, 64);
+
+// TODO: IGNITE-1258
+// registerPredefinedType(DrSenderAttributes.class, 65);
+// registerPredefinedType(DrSenderRemoteAttributes.class, 66);
+//
+// registerPredefinedType(InteropClusterNode.class, 67);
+// registerPredefinedType(InteropClusterMetrics.class, 68);
+// registerPredefinedType(InteropTransactionMetrics.class, 69);
+// registerPredefinedType(InteropMetadata.class, 70);
+//
+// registerPredefinedType(InteropDotNetConfiguration.class, 71);
+// registerPredefinedType(InteropDotNetPortableConfiguration.class, 72);
+// registerPredefinedType(InteropDotNetPortableTypeConfiguration.class, 73);
+// registerPredefinedType(InteropIgniteProxy.class, 74);
+// registerPredefinedType(InteropCacheMetrics.class, 75);
+// registerPredefinedType(InteropProductLicence.class, 78);
+ }
+
+ /**
+ * @param marsh Portable marshaller.
+ * @throws PortableException In case of error.
+ */
+ public void configure(PortableMarshaller marsh) throws PortableException {
+ if (marsh == null)
+ return;
+
+ this.marsh = marsh;
+ marshCtx = marsh.getContext();
+
+ assert marshCtx != null;
+
+ optmMarsh.setContext(marshCtx);
+
+ PortableIdMapper globalIdMapper = marsh.getIdMapper();
+ PortableSerializer globalSerializer = marsh.getSerializer();
+ boolean globalUseTs = marsh.isUseTimestamp();
+ boolean globalMetaDataEnabled = marsh.isMetaDataEnabled();
+ boolean globalKeepDeserialized = marsh.isKeepDeserialized();
+
+ TypeDescriptors descs = new TypeDescriptors();
+
+ if (marsh.getClassNames() != null) {
+ PortableIdMapper idMapper = new IdMapperWrapper(globalIdMapper);
+
+ for (String clsName : marsh.getClassNames()) {
+ if (clsName.endsWith(".*")) { // Package wildcard
+ String pkgName = clsName.substring(0, clsName.length() - 2);
+
+ for (String clsName0 : classesInPackage(pkgName))
+ descs.add(clsName0, idMapper, null, null, globalUseTs, globalMetaDataEnabled,
+ globalKeepDeserialized, true);
+ }
+ else // Regular single class
+ descs.add(clsName, idMapper, null, null, globalUseTs, globalMetaDataEnabled,
+ globalKeepDeserialized, true);
+ }
+ }
+
+ if (marsh.getTypeConfigurations() != null) {
+ for (PortableTypeConfiguration typeCfg : marsh.getTypeConfigurations()) {
+ String clsName = typeCfg.getClassName();
+
+ if (clsName == null)
+ throw new PortableException("Class name is required for portable type configuration.");
+
+ PortableIdMapper idMapper = globalIdMapper;
+
+ if (typeCfg.getIdMapper() != null)
+ idMapper = typeCfg.getIdMapper();
+
+ idMapper = new IdMapperWrapper(idMapper);
+
+ PortableSerializer serializer = globalSerializer;
+
+ if (typeCfg.getSerializer() != null)
+ serializer = typeCfg.getSerializer();
+
+ boolean useTs = typeCfg.isUseTimestamp() != null ? typeCfg.isUseTimestamp() : globalUseTs;
+ boolean metaDataEnabled = typeCfg.isMetaDataEnabled() != null ? typeCfg.isMetaDataEnabled() :
+ globalMetaDataEnabled;
+ boolean keepDeserialized = typeCfg.isKeepDeserialized() != null ? typeCfg.isKeepDeserialized() :
+ globalKeepDeserialized;
+
+ if (clsName.endsWith(".*")) {
+ String pkgName = clsName.substring(0, clsName.length() - 2);
+
+ for (String clsName0 : classesInPackage(pkgName))
+ descs.add(clsName0, idMapper, serializer, typeCfg.getAffinityKeyFieldName(), useTs,
+ metaDataEnabled, keepDeserialized, true);
+ }
+ else
+ descs.add(clsName, idMapper, serializer, typeCfg.getAffinityKeyFieldName(), useTs,
+ metaDataEnabled, keepDeserialized, false);
+ }
+ }
+
+ for (TypeDescriptor desc : descs.descriptors())
+ registerUserType(desc.clsName, desc.idMapper, desc.serializer, desc.affKeyFieldName, desc.useTs,
+ desc.metadataEnabled, desc.keepDeserialized);
+ }
+
+ /**
+ * @param pkgName Package name.
+ * @return Class names.
+ */
+ @SuppressWarnings("ConstantConditions")
+ private static Iterable<String> classesInPackage(String pkgName) {
+ assert pkgName != null;
+
+ Collection<String> clsNames = new ArrayList<>();
+
+ ClassLoader ldr = U.gridClassLoader();
+
+ if (ldr instanceof URLClassLoader) {
+ String pkgPath = pkgName.replaceAll("\\.", "/");
+
+ URL[] urls = ((URLClassLoader)ldr).getURLs();
+
+ for (URL url : urls) {
+ String proto = url.getProtocol().toLowerCase();
+
+ if ("file".equals(proto)) {
+ try {
+ File cpElement = new File(url.toURI());
+
+ if (cpElement.isDirectory()) {
+ File pkgDir = new File(cpElement, pkgPath);
+
+ if (pkgDir.isDirectory()) {
+ for (File file : pkgDir.listFiles()) {
+ String fileName = file.getName();
+
+ if (file.isFile() && fileName.toLowerCase().endsWith(".class"))
+ clsNames.add(pkgName + '.' + fileName.substring(0, fileName.length() - 6));
+ }
+ }
+ }
+ else if (cpElement.isFile()) {
+ try {
+ JarFile jar = new JarFile(cpElement);
+
+ Enumeration<JarEntry> entries = jar.entries();
+
+ while (entries.hasMoreElements()) {
+ String entry = entries.nextElement().getName();
+
+ if (entry.startsWith(pkgPath) && entry.endsWith(".class")) {
+ String clsName = entry.substring(pkgPath.length() + 1, entry.length() - 6);
+
+ if (!clsName.contains("/") && !clsName.contains("\\"))
+ clsNames.add(pkgName + '.' + clsName);
+ }
+ }
+ }
+ catch (IOException ignored) {
+ // No-op.
+ }
+ }
+ }
+ catch (URISyntaxException ignored) {
+ // No-op.
+ }
+ }
+ }
+ }
+
+ return clsNames;
+ }
+
+ /**
+ * @param cls Class.
+ * @return Class descriptor.
+ * @throws PortableException In case of error.
+ */
+ public PortableClassDescriptor descriptorForClass(Class<?> cls)
+ throws PortableException {
+ assert cls != null;
+
+ PortableClassDescriptor desc = descByCls.get(cls);
+
+ if (desc == null || !desc.isRegistered())
+ desc = registerClassDescriptor(cls);
+
+ return desc;
+ }
+
+ /**
+ * @param userType User type or not.
+ * @param typeId Type ID.
+ * @param ldr Class loader.
+ * @return Class descriptor.
+ */
+ public PortableClassDescriptor descriptorForTypeId(boolean userType, int typeId, ClassLoader ldr) {
+ assert typeId != GridPortableMarshaller.UNREGISTERED_TYPE_ID;
+
+ PortableClassDescriptor desc = userType ? userTypes.get(typeId) : predefinedTypes.get(typeId);
+
+ if (desc != null)
+ return desc;
+
+ Class cls;
+
+ try {
+ cls = marshCtx.getClass(typeId, ldr);
+
+ desc = descByCls.get(cls);
+ }
+ catch (ClassNotFoundException e) {
+ throw new PortableInvalidClassException(e);
+ }
+ catch (IgniteCheckedException e) {
+ throw new PortableException("Failed resolve class for ID: " + typeId, e);
+ }
+
+ if (desc == null) {
+ desc = registerClassDescriptor(cls);
+
+ assert desc.typeId() == typeId;
+ }
+
+ return desc;
+ }
+
+ /**
+ * Creates and registers {@link PortableClassDescriptor} for the given {@code class}.
+ *
+ * @param cls Class.
+ * @return Class descriptor.
+ */
+ private PortableClassDescriptor registerClassDescriptor(Class<?> cls) {
+ PortableClassDescriptor desc;
+
+ String clsName = cls.getName();
+
+ if (marshCtx.isSystemType(clsName)) {
+ desc = new PortableClassDescriptor(this,
+ cls,
+ false,
+ clsName.hashCode(),
+ clsName,
+ BASIC_CLS_ID_MAPPER,
+ null,
+ marsh.isUseTimestamp(),
+ marsh.isMetaDataEnabled(),
+ marsh.isKeepDeserialized());
+
+ PortableClassDescriptor old = descByCls.putIfAbsent(cls, desc);
+
+ if (old != null)
+ desc = old;
+ }
+ else
+ desc = registerUserClassDescriptor(cls);
+
+ return desc;
+ }
+
+ /**
+ * Creates and registers {@link PortableClassDescriptor} for the given user {@code class}.
+ *
+ * @param cls Class.
+ * @return Class descriptor.
+ */
+ private PortableClassDescriptor registerUserClassDescriptor(Class<?> cls) {
+ PortableClassDescriptor desc;
+
+ boolean registered;
+
+ String typeName = typeName(cls.getName());
+
+ PortableIdMapper idMapper = idMapper(typeName);
+
+ int typeId = idMapper.typeId(typeName);
+
+ try {
+ registered = marshCtx.registerClass(typeId, cls);
+
+ } catch (IgniteCheckedException e) {
+ throw new PortableException("Failed to register class.", e);
+ }
+
+ desc = new PortableClassDescriptor(this,
+ cls,
+ true,
+ typeId,
+ typeName,
+ idMapper,
+ null,
+ marsh.isUseTimestamp(),
+ marsh.isMetaDataEnabled(),
+ marsh.isKeepDeserialized(),
+ registered);
+
+ // perform put() instead of putIfAbsent() because "registered" flag may have been changed.
+ userTypes.put(typeId, desc);
+ descByCls.put(cls, desc);
+
+ return desc;
+ }
+
+ /**
+ * @param cls Collection class.
+ * @return Collection type ID.
+ */
+ public byte collectionType(Class<? extends Collection> cls) {
+ assert cls != null;
+
+ Byte type = colTypes.get(cls);
+
+ if (type != null)
+ return type;
+
+ return Set.class.isAssignableFrom(cls) ? GridPortableMarshaller.USER_SET : GridPortableMarshaller.USER_COL;
+ }
+
+ /**
+ * @param cls Map class.
+ * @return Map type ID.
+ */
+ public byte mapType(Class<? extends Map> cls) {
+ assert cls != null;
+
+ Byte type = mapTypes.get(cls);
+
+ return type != null ? type : GridPortableMarshaller.USER_COL;
+ }
+
+ /**
+ * @param typeName Type name.
+ * @return Type ID.
+ */
+ public int typeId(String typeName) {
+ int id;
+
+ if (marshCtx.isSystemType(typeName))
+ id = typeName.hashCode();
+
+ else {
+ typeName = typeName(typeName);
+
+ id = idMapper(typeName).typeId(typeName);
+ }
+
+ return id;
+ }
+
+ /**
+ * @param cls Class.
+ * @return Type ID.
+ * @throws PortableException In case of error.
+ */
+ public Type typeId(Class cls) throws PortableException {
+ String clsName = cls.getName();
+
+ if (marshCtx.isSystemType(clsName))
+ return new Type(clsName.hashCode(), true);
+
+ if (predefinedClasses.contains(cls))
+ return new Type(DFLT_ID_MAPPER.typeId(typeName(clsName)), true);
+
+ PortableClassDescriptor desc = descByCls.get(cls);
+
+ boolean registered = desc != null && desc.isRegistered();
+
+ if (!registered)
+ // forces to register the class and fill up all required data structures
+ desc = registerUserClassDescriptor(cls);
+
+ return new Type(desc.typeId(), desc.isRegistered());
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @param fieldName Field name.
+ * @return Field ID.
+ */
+ public int fieldId(int typeId, String fieldName) {
+ return idMapper(typeId).fieldId(typeId, fieldName);
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @return Instance of ID mapper.
+ */
+ public PortableIdMapper idMapper(int typeId) {
+ PortableIdMapper idMapper = mappers.get(typeId);
+
+ if (idMapper != null)
+ return idMapper;
+
+ if (userTypes.containsKey(typeId) || predefinedTypes.containsKey(typeId))
+ return DFLT_ID_MAPPER;
+
+ return BASIC_CLS_ID_MAPPER;
+ }
+
+ /**
+ * @param typeName Type name.
+ * @return Instance of ID mapper.
+ */
+ private PortableIdMapper idMapper(String typeName) {
+ PortableIdMapper idMapper = typeMappers.get(typeName);
+
+ return idMapper != null ? idMapper : DFLT_ID_MAPPER;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeExternal(ObjectOutput out) throws IOException {
+ U.writeString(out, gridName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ gridName = U.readString(in);
+ }
+
+ /**
+ * @return Portable context.
+ * @throws ObjectStreamException In case of error.
+ */
+ protected Object readResolve() throws ObjectStreamException {
+ try {
+ IgniteKernal g = IgnitionEx.gridx(gridName);
+
+ if (g == null)
+ throw new IllegalStateException("Failed to find grid for name: " + gridName);
+
+ return ((CacheObjectPortableProcessorImpl)g.context().cacheObjects()).portableContext();
+ }
+ catch (IllegalStateException e) {
+ throw U.withCause(new InvalidObjectException(e.getMessage()), e);
+ }
+ }
+
+ /**
+ * @param cls Class.
+ * @param id Type ID.
+ * @return PortableClassDescriptor.
+ */
+ private PortableClassDescriptor registerPredefinedType(Class<?> cls, int id) {
+ PortableClassDescriptor desc = new PortableClassDescriptor(
+ this,
+ cls,
+ false,
+ id,
+ typeName(cls.getName()),
+ DFLT_ID_MAPPER,
+ null,
+ false,
+ false,
+ false
+ );
+
+ predefinedClasses.add(cls);
+
+ predefinedTypes.put(id, desc);
+ descByCls.put(cls, desc);
+
+ return desc;
+ }
+
+ /**
+ * @param clsName Class name.
+ * @param idMapper ID mapper.
+ * @param serializer Serializer.
+ * @param affKeyFieldName Affinity key field name.
+ * @param useTs Use timestamp flag.
+ * @param metaDataEnabled Metadata enabled flag.
+ * @param keepDeserialized Keep deserialized flag.
+ * @throws PortableException In case of error.
+ */
+ @SuppressWarnings("ErrorNotRethrown")
+ public void registerUserType(String clsName,
+ PortableIdMapper idMapper,
+ @Nullable PortableSerializer serializer,
+ @Nullable String affKeyFieldName,
+ boolean useTs,
+ boolean metaDataEnabled,
+ boolean keepDeserialized)
+ throws PortableException {
+ assert idMapper != null;
+
+ Class<?> cls = null;
+
+ try {
+ cls = Class.forName(clsName);
+ }
+ catch (ClassNotFoundException | NoClassDefFoundError ignored) {
+ // No-op.
+ }
+
+ int id = idMapper.typeId(clsName);
+
+ if (mappers.put(id, idMapper) != null)
+ throw new PortableException("Duplicate type ID [clsName=" + clsName + ", id=" + id + ']');
+
+ if (useTs)
+ usingTs.add(id);
+
+ String typeName = typeName(clsName);
+
+ typeMappers.put(typeName, idMapper);
+
+ metaEnabled.put(id, metaDataEnabled);
+
+ Map<String, String> fieldsMeta = null;
+
+ if (cls != null) {
+ PortableClassDescriptor desc = new PortableClassDescriptor(
+ this,
+ cls,
+ true,
+ id,
+ typeName,
+ idMapper,
+ serializer,
+ useTs,
+ metaDataEnabled,
+ keepDeserialized);
+
+ fieldsMeta = desc.fieldsMeta();
+
+ userTypes.put(id, desc);
+ descByCls.put(cls, desc);
+ }
+
+ metaHnd.addMeta(id, new PortableMetaDataImpl(typeName, fieldsMeta, affKeyFieldName));
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @return Meta data.
+ * @throws PortableException In case of error.
+ */
+ @Nullable public PortableMetadata metaData(int typeId) throws PortableException {
+ return metaHnd != null ? metaHnd.metadata(typeId) : null;
+ }
+
+ /**
+ * @return Whether meta data is globally enabled.
+ */
+ boolean isMetaDataEnabled() {
+ return marsh.isMetaDataEnabled();
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @return Whether meta data is enabled.
+ */
+ boolean isMetaDataEnabled(int typeId) {
+ Boolean enabled = metaEnabled.get(typeId);
+
+ return enabled != null ? enabled : true;
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @param metaHashSum Meta data hash sum.
+ * @return Whether meta is changed.
+ */
+ boolean isMetaDataChanged(int typeId, @Nullable Integer metaHashSum) {
+ if (metaHashSum == null)
+ return false;
+
+ Collection<Integer> hist = metaDataCache.get(typeId);
+
+ if (hist == null) {
+ Collection<Integer> old = metaDataCache.putIfAbsent(typeId, hist = new GridConcurrentHashSet<>());
+
+ if (old != null)
+ hist = old;
+ }
+
+ return hist.add(metaHashSum);
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @param typeName Type name.
+ * @param fields Fields map.
+ * @throws PortableException In case of error.
+ */
+ void updateMetaData(int typeId, String typeName, Map<String, String> fields) throws PortableException {
+ updateMetaData(typeId, new PortableMetaDataImpl(typeName, fields, null));
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @param meta Meta data.
+ * @throws PortableException In case of error.
+ */
+ public void updateMetaData(int typeId, PortableMetaDataImpl meta) throws PortableException {
+ metaHnd.addMeta(typeId, meta);
+ }
+
+ /**
+ * @return Use timestamp flag.
+ */
+ public boolean isUseTimestamp() {
+ return marsh.isUseTimestamp();
+ }
+
+ /**
+ * @param typeId Type ID.
+ * @return If timestamp used.
+ */
+ public boolean isUseTimestamp(int typeId) {
+ return usingTs.contains(typeId);
+ }
+
+ /**
+ * @return Whether to convert string to UTF8 bytes.
+ */
+ public boolean isConvertString() {
+ return marsh.isConvertStringToBytes();
+ }
+
+ /**
+ * Returns whether {@code cls} is predefined in the context or not.
+ *
+ * @param cls Class.
+ * @return {@code true} if predefined, {@code false} otherwise.
+ */
+ public boolean isPredefinedClass(Class<?> cls) {
+ return predefinedClasses.contains(cls);
+ }
+
+ /**
+ * Returns instance of {@link OptimizedMarshaller}.
+ *
+ * @return Optimized marshaller.
+ */
+ OptimizedMarshaller optimizedMarsh() {
+ return optmMarsh;
+ }
+
+ /**
+ * @param clsName Class name.
+ * @return Type name.
+ */
+ public static String typeName(String clsName) {
+ assert clsName != null;
+
+ int idx = clsName.lastIndexOf('$');
+
+ String typeName;
+
+ if (idx >= 0) {
+ typeName = clsName.substring(idx + 1);
+
+ try {
+ Integer.parseInt(typeName);
+
+ // This is an anonymous class. Don't cut off enclosing class name for it.
+ idx = -1;
+ }
+ catch (NumberFormatException e) {
+ return typeName;
+ }
+ }
+
+ if (idx < 0)
+ idx = clsName.lastIndexOf('.');
+
+ return idx >= 0 ? clsName.substring(idx + 1) : clsName;
+ }
+
+ /**
+ * @param str String.
+ * @return Hash code for given string converted to lower case.
+ */
+ private static int lowerCaseHashCode(String str) {
+ int len = str.length();
+
+ int h = 0;
+
+ for (int i = 0; i < len; i++) {
+ int c = str.charAt(i);
+
+ c = c <= MAX_LOWER_CASE_CHAR ? LOWER_CASE_CHARS[c] : Character.toLowerCase(c);
+
+ h = 31 * h + c;
+ }
+
+ return h;
+ }
+
+ /**
+ */
+ private static class IdMapperWrapper implements PortableIdMapper {
+ /** */
+ private final PortableIdMapper mapper;
+
+ /**
+ * @param mapper Custom ID mapper.
+ */
+ private IdMapperWrapper(@Nullable PortableIdMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ /** {@inheritDoc} */
+ @Override public int typeId(String clsName) {
+ int id = 0;
+
+ if (mapper != null)
+ id = mapper.typeId(clsName);
+
+ return id != 0 ? id : lowerCaseHashCode(typeName(clsName));
+ }
+
+ /** {@inheritDoc} */
+ @Override public int fieldId(int typeId, String fieldName) {
+ int id = 0;
+
+ if (mapper != null)
+ id = mapper.fieldId(typeId, fieldName);
+
+ return id != 0 ? id : lowerCaseHashCode(fieldName);
+ }
+ }
+
+ private static class BasicClassIdMapper implements PortableIdMapper {
+ /** {@inheritDoc} */
+ @Override public int typeId(String clsName) {
+ return clsName.hashCode();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int fieldId(int typeId, String fieldName) {
+ return lowerCaseHashCode(fieldName);
+ }
+ }
+ /**
+ * Type descriptors.
+ */
+ private static class TypeDescriptors {
+ /** Descriptors map. */
+ private final Map<String, TypeDescriptor> descs = new HashMap<>();
+
+ /**
+ * Add type descriptor.
+ *
+ * @param clsName Class name.
+ * @param idMapper ID mapper.
+ * @param serializer Serializer.
+ * @param affKeyFieldName Affinity key field name.
+ * @param useTs Use timestamp flag.
+ * @param metadataEnabled Metadata enabled flag.
+ * @param keepDeserialized Keep deserialized flag.
+ * @param canOverride Whether this descriptor can be override.
+ * @throws PortableException If failed.
+ */
+ private void add(String clsName,
+ PortableIdMapper idMapper,
+ PortableSerializer serializer,
+ String affKeyFieldName,
+ boolean useTs,
+ boolean metadataEnabled,
+ boolean keepDeserialized,
+ boolean canOverride)
+ throws PortableException {
+ TypeDescriptor desc = new TypeDescriptor(clsName,
+ idMapper,
+ serializer,
+ affKeyFieldName,
+ useTs,
+ metadataEnabled,
+ keepDeserialized,
+ canOverride);
+
+ TypeDescriptor oldDesc = descs.get(clsName);
+
+ if (oldDesc == null)
+ descs.put(clsName, desc);
+ else
+ oldDesc.override(desc);
+ }
+
+ /**
+ * Get all collected descriptors.
+ *
+ * @return Descriptors.
+ */
+ private Iterable<TypeDescriptor> descriptors() {
+ return descs.values();
+ }
+ }
+
+ /**
+ * Type descriptor.
+ */
+ private static class TypeDescriptor {
+ /** Class name. */
+ private final String clsName;
+
+ /** ID mapper. */
+ private PortableIdMapper idMapper;
+
+ /** Serializer. */
+ private PortableSerializer serializer;
+
+ /** Affinity key field name. */
+ private String affKeyFieldName;
+
+ /** Use timestamp flag. */
+ private boolean useTs;
+
+ /** Metadata enabled flag. */
+ private boolean metadataEnabled;
+
+ /** Keep deserialized flag. */
+ private boolean keepDeserialized;
+
+ /** Whether this descriptor can be override. */
+ private boolean canOverride;
+
+ /**
+ * Constructor.
+ *
+ * @param clsName Class name.
+ * @param idMapper ID mapper.
+ * @param serializer Serializer.
+ * @param affKeyFieldName Affinity key field name.
+ * @param useTs Use timestamp flag.
+ * @param metadataEnabled Metadata enabled flag.
+ * @param keepDeserialized Keep deserialized flag.
+ * @param canOverride Whether this descriptor can be override.
+ */
+ private TypeDescriptor(String clsName, PortableIdMapper idMapper, PortableSerializer serializer,
+ String affKeyFieldName, boolean useTs, boolean metadataEnabled, boolean keepDeserialized,
+ boolean canOverride) {
+ this.clsName = clsName;
+ this.idMapper = idMapper;
+ this.serializer = serializer;
+ this.affKeyFieldName = affKeyFieldName;
+ this.useTs = useTs;
+ this.metadataEnabled = metadataEnabled;
+ this.keepDeserialized = keepDeserialized;
+ this.canOverride = canOverride;
+ }
+
+ /**
+ * Override portable class descriptor.
+ *
+ * @param other Other descriptor.
+ * @throws PortableException If failed.
+ */
+ private void override(TypeDescriptor other) throws PortableException {
+ assert clsName.equals(other.clsName);
+
+ if (canOverride) {
+ idMapper = other.idMapper;
+ serializer = other.serializer;
+ affKeyFieldName = other.affKeyFieldName;
+ useTs = other.useTs;
+ metadataEnabled = other.metadataEnabled;
+ keepDeserialized = other.keepDeserialized;
+ canOverride = other.canOverride;
+ }
+ else if (!other.canOverride)
+ throw new PortableException("Duplicate explicit class definition in configuration: " + clsName);
+ }
+ }
+
+ /**
+ * Type id wrapper.
+ */
+ static class Type {
+ /** Type id*/
+ private int id;
+
+ /** Whether the following type is registered in a cache or not */
+ private boolean registered;
+
+ public Type(int id, boolean registered) {
+ this.id = id;
+ this.registered = registered;
+ }
+
+ public int id() {
+ return id;
+ }
+
+ public boolean registered() {
+ return registered;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java
new file mode 100644
index 0000000..7fa04e8
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableEnumArrayLazyValue.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+/**
+ *
+ */
+class PortableEnumArrayLazyValue extends PortableAbstractLazyValue {
+ /** */
+ private final int len;
+
+ /** */
+ private final int compTypeId;
+
+ /** */
+ private final String clsName;
+
+ /**
+ * @param reader Reader.
+ */
+ protected PortableEnumArrayLazyValue(PortableBuilderReader reader) {
+ super(reader, reader.position() - 1);
+
+ int typeId = reader.readInt();
+
+ if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) {
+ clsName = reader.readString();
+
+ Class cls;
+
+ try {
+ // TODO: IGNITE-1272 - Is class loader needed here?
+ cls = U.forName(reader.readString(), null);
+ }
+ catch (ClassNotFoundException e) {
+ throw new PortableInvalidClassException("Failed to load the class: " + clsName, e);
+ }
+
+ compTypeId = reader.portableContext().descriptorForClass(cls).typeId();
+ }
+ else {
+ compTypeId = typeId;
+ clsName = null;
+ }
+
+ int size = reader.readInt();
+
+ for (int i = 0; i < size; i++)
+ reader.skipValue();
+
+ len = reader.position() - valOff;
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object init() {
+ reader.position(valOff + 1);
+
+ //skipping component type id
+ reader.readInt();
+
+ int size = reader.readInt();
+
+ PortableBuilderEnum[] res = new PortableBuilderEnum[size];
+
+ for (int i = 0; i < size; i++) {
+ byte flag = reader.readByte();
+
+ if (flag == GridPortableMarshaller.NULL)
+ continue;
+
+ if (flag != GridPortableMarshaller.ENUM)
+ throw new PortableException("Invalid flag value: " + flag);
+
+ res[i] = new PortableBuilderEnum(reader);
+ }
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ if (val != null) {
+ if (clsName != null)
+ ctx.writeArray(writer, GridPortableMarshaller.ENUM_ARR, (Object[])val, clsName);
+ else
+ ctx.writeArray(writer, GridPortableMarshaller.ENUM_ARR, (Object[])val, compTypeId);
+
+ return;
+ }
+
+ writer.write(reader.array(), valOff, len);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java
new file mode 100644
index 0000000..56dfba9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyArrayList.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazyArrayList extends AbstractList<Object> implements PortableBuilderSerializationAware {
+ /** */
+ private final PortableBuilderReader reader;
+
+ /** */
+ private final int off;
+
+ /** */
+ private List<Object> delegate;
+
+ /**
+ * @param reader Reader.
+ * @param size Size,
+ */
+ PortableLazyArrayList(PortableBuilderReader reader, int size) {
+ this.reader = reader;
+ off = reader.position() - 1/* flag */ - 4/* size */ - 1/* col type */;
+
+ assert size >= 0;
+
+ for (int i = 0; i < size; i++)
+ reader.skipValue();
+ }
+
+ /**
+ *
+ */
+ private void ensureDelegateInit() {
+ if (delegate == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+ delegate = new ArrayList<>(size);
+
+ for (int i = 0; i < size; i++)
+ delegate.add(reader.parseValue());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object get(int idx) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.get(idx));
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean add(Object o) {
+ ensureDelegateInit();
+
+ return delegate.add(o);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void add(int idx, Object element) {
+ ensureDelegateInit();
+
+ delegate.add(idx, element);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object set(int idx, Object element) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.set(idx, element));
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object remove(int idx) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.remove(idx));
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clear() {
+ if (delegate == null)
+ delegate = new ArrayList<>();
+ else
+ delegate.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addAll(int idx, Collection<?> c) {
+ return delegate.addAll(idx, c);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void removeRange(int fromIdx, int toIdx) {
+ ensureDelegateInit();
+
+ delegate.subList(fromIdx, toIdx).clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size() {
+ if (delegate == null)
+ return reader.readIntAbsolute(off + 1);
+
+ return delegate.size();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ if (delegate == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+
+ writer.write(reader.array(), off, hdrSize);
+
+ reader.position(off + hdrSize);
+
+ for (int i = 0; i < size; i++) {
+ Object o = reader.parseValue();
+
+ ctx.writeValue(writer, o);
+ }
+ }
+ else {
+ writer.writeByte(GridPortableMarshaller.COL);
+ writer.writeInt(delegate.size());
+
+ byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+ writer.writeByte(colType);
+
+ for (Object o : delegate)
+ ctx.writeValue(writer, o);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java
new file mode 100644
index 0000000..9395aeb
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyLinkedList.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazyLinkedList extends AbstractList<Object> implements PortableBuilderSerializationAware {
+ /** */
+ private final PortableBuilderReader reader;
+
+ /** */
+ private final int off;
+
+ /** */
+ private List<Object> delegate;
+
+ /**
+ * @param reader Reader.
+ * @param size Size,
+ */
+ PortableLazyLinkedList(PortableBuilderReader reader, int size) {
+ this.reader = reader;
+ off = reader.position() - 1/* flag */ - 4/* size */ - 1/* col type */;
+
+ assert size >= 0;
+
+ for (int i = 0; i < size; i++)
+ reader.skipValue();
+ }
+
+ /**
+ *
+ */
+ private void ensureDelegateInit() {
+ if (delegate == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+ delegate = new LinkedList<>();
+
+ for (int i = 0; i < size; i++)
+ delegate.add(reader.parseValue());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object get(int idx) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.get(idx));
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean add(Object o) {
+ ensureDelegateInit();
+
+ return delegate.add(o);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void add(int idx, Object element) {
+ ensureDelegateInit();
+
+ delegate.add(idx, element);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object set(int idx, Object element) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.set(idx, element));
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object remove(int idx) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.remove(idx));
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clear() {
+ if (delegate == null)
+ delegate = new LinkedList<>();
+ else
+ delegate.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean addAll(int idx, Collection<?> c) {
+ ensureDelegateInit();
+
+ return delegate.addAll(idx, c);
+ }
+
+ /** {@inheritDoc} */
+ @Override protected void removeRange(int fromIdx, int toIdx) {
+ ensureDelegateInit();
+
+ delegate.subList(fromIdx, toIdx).clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size() {
+ if (delegate == null)
+ return reader.readIntAbsolute(off + 1);
+
+ return delegate.size();
+ }
+
+ /** {@inheritDoc} */
+ @Override public ListIterator<Object> listIterator(final int idx) {
+ ensureDelegateInit();
+
+ return new ListIterator<Object>() {
+ /** */
+ private final ListIterator<Object> delegate = PortableLazyLinkedList.super.listIterator(idx);
+
+ @Override public boolean hasNext() {
+ return delegate.hasNext();
+ }
+
+ @Override public Object next() {
+ return PortableUtils.unwrapLazy(delegate.next());
+ }
+
+ @Override public boolean hasPrevious() {
+ return delegate.hasPrevious();
+ }
+
+ @Override public Object previous() {
+ return PortableUtils.unwrapLazy(delegate.previous());
+ }
+
+ @Override public int nextIndex() {
+ return delegate.nextIndex();
+ }
+
+ @Override public int previousIndex() {
+ return delegate.previousIndex();
+ }
+
+ @Override public void remove() {
+ delegate.remove();
+ }
+
+ @Override public void set(Object o) {
+ delegate.set(o);
+ }
+
+ @Override public void add(Object o) {
+ delegate.add(o);
+ }
+ };
+ }
+
+ /** {@inheritDoc} */
+ @Override public Iterator<Object> iterator() {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazyIterator(super.iterator());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ if (delegate == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+ writer.write(reader.array(), off, hdrSize);
+
+ reader.position(off + hdrSize);
+
+ for (int i = 0; i < size; i++) {
+ Object o = reader.parseValue();
+
+ ctx.writeValue(writer, o);
+ }
+ }
+ else {
+ writer.writeByte(GridPortableMarshaller.COL);
+ writer.writeInt(delegate.size());
+
+ byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+ writer.writeByte(colType);
+
+ for (Object o : delegate)
+ ctx.writeValue(writer, o);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java
new file mode 100644
index 0000000..e7f7727
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMap.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.jetbrains.annotations.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazyMap extends AbstractMap<Object, Object> implements PortableBuilderSerializationAware {
+ /** */
+ private final PortableBuilderReader reader;
+
+ /** */
+ private final int off;
+
+ /** */
+ private Map<Object, Object> delegate;
+
+ /**
+ * @param reader Reader.
+ * @param off Offset.
+ */
+ private PortableLazyMap(PortableBuilderReader reader, int off) {
+ this.reader = reader;
+ this.off = off;
+ }
+
+ /**
+ * @param reader Reader.
+ * @return PortableLazyMap.
+ */
+ @Nullable public static PortableLazyMap parseMap(PortableBuilderReader reader) {
+ int off = reader.position() - 1;
+
+ int size = reader.readInt();
+
+ reader.skip(1); // map type.
+
+ for (int i = 0; i < size; i++) {
+ reader.skipValue(); // skip key
+ reader.skipValue(); // skip value
+ }
+
+ return new PortableLazyMap(reader, off);
+ }
+
+ /**
+ *
+ */
+ private void ensureDelegateInit() {
+ if (delegate == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+ delegate = new LinkedHashMap<>();
+
+ for (int i = 0; i < size; i++)
+ delegate.put(PortableUtils.unwrapLazy(reader.parseValue()), reader.parseValue());
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ if (delegate == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+ writer.write(reader.array(), off, hdrSize);
+
+ reader.position(off + hdrSize);
+
+ for (int i = 0; i < size; i++) {
+ ctx.writeValue(writer, reader.parseValue()); // key
+ ctx.writeValue(writer, reader.parseValue()); // value
+ }
+ }
+ else {
+ writer.writeByte(GridPortableMarshaller.MAP);
+ writer.writeInt(delegate.size());
+
+ byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+
+ writer.writeByte(colType);
+
+ for (Entry<Object, Object> entry : delegate.entrySet()) {
+ ctx.writeValue(writer, entry.getKey());
+ ctx.writeValue(writer, entry.getValue());
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override public int size() {
+ if (delegate == null)
+ return reader.readIntAbsolute(off + 1);
+
+ return delegate.size();
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean containsKey(Object key) {
+ ensureDelegateInit();
+
+ return delegate.containsKey(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public boolean containsValue(Object val) {
+ return values().contains(val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Set<Object> keySet() {
+ ensureDelegateInit();
+
+ return delegate.keySet();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void clear() {
+ if (delegate == null)
+ delegate = new LinkedHashMap<>();
+ else
+ delegate.clear();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object get(Object key) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.get(key));
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object put(Object key, Object val) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.put(key, val));
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object remove(Object key) {
+ ensureDelegateInit();
+
+ return PortableUtils.unwrapLazy(delegate.remove(key));
+ }
+
+ /** {@inheritDoc} */
+ @Override public Set<Entry<Object, Object>> entrySet() {
+ ensureDelegateInit();
+
+ return new AbstractSet<Entry<Object, Object>>() {
+ @Override public boolean contains(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override public Iterator<Entry<Object, Object>> iterator() {
+ return new Iterator<Entry<Object, Object>>() {
+ /** */
+ private final Iterator<Entry<Object, Object>> itr = delegate.entrySet().iterator();
+
+ @Override public boolean hasNext() {
+ return itr.hasNext();
+ }
+
+ @Override public Entry<Object, Object> next() {
+ Entry<Object, Object> res = itr.next();
+
+ final Object val = res.getValue();
+
+ if (val instanceof PortableLazyValue) {
+ return new SimpleEntry<Object, Object>(res.getKey(), val) {
+ private static final long serialVersionUID = 0L;
+
+ @Override public Object getValue() {
+ return ((PortableLazyValue)val).value();
+ }
+ };
+ }
+
+ return res;
+ }
+
+ @Override public void remove() {
+ itr.remove();
+ }
+ };
+ }
+
+ @Override public int size() {
+ return delegate.size();
+ }
+ };
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java
new file mode 100644
index 0000000..389ab33
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyMapEntry.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazyMapEntry implements Map.Entry<Object, Object>, PortableBuilderSerializationAware {
+ /** */
+ private final Object key;
+
+ /** */
+ private Object val;
+
+ /**
+ * @param reader GridMutablePortableReader
+ */
+ PortableLazyMapEntry(PortableBuilderReader reader) {
+ key = reader.parseValue();
+ val = reader.parseValue();
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object getKey() {
+ return PortableUtils.unwrapLazy(key);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object getValue() {
+ return PortableUtils.unwrapLazy(val);
+ }
+
+ /** {@inheritDoc} */
+ @Override public Object setValue(Object val) {
+ Object res = getValue();
+
+ this.val = val;
+
+ return res;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ writer.writeByte(GridPortableMarshaller.MAP_ENTRY);
+
+ ctx.writeValue(writer, key);
+ ctx.writeValue(writer, val);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java
new file mode 100644
index 0000000..d5a59a0
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazySet.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.typedef.internal.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+class PortableLazySet extends PortableAbstractLazyValue {
+ /** */
+ private final int off;
+
+ /**
+ * @param reader Reader.
+ * @param size Size.
+ */
+ PortableLazySet(PortableBuilderReader reader, int size) {
+ super(reader, reader.position() - 1);
+
+ off = reader.position() - 1/* flag */ - 4/* size */ - 1/* col type */;
+
+ assert size >= 0;
+
+ for (int i = 0; i < size; i++)
+ reader.skipValue();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ if (val == null) {
+ int size = reader.readIntAbsolute(off + 1);
+
+ int hdrSize = 1 /* flag */ + 4 /* size */ + 1 /* col type */;
+ writer.write(reader.array(), off, hdrSize);
+
+ reader.position(off + hdrSize);
+
+ for (int i = 0; i < size; i++) {
+ Object o = reader.parseValue();
+
+ ctx.writeValue(writer, o);
+ }
+ }
+ else {
+ Collection<Object> c = (Collection<Object>)val;
+
+ writer.writeByte(GridPortableMarshaller.COL);
+ writer.writeInt(c.size());
+
+ byte colType = reader.array()[off + 1 /* flag */ + 4 /* size */];
+ writer.writeByte(colType);
+
+ for (Object o : c)
+ ctx.writeValue(writer, o);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object init() {
+ int size = reader.readIntAbsolute(off + 1);
+
+ reader.position(off + 1/* flag */ + 4/* size */ + 1/* col type */);
+
+ Set<Object> res = U.newLinkedHashSet(size);
+
+ for (int i = 0; i < size; i++)
+ res.add(PortableUtils.unwrapLazy(reader.parseValue()));
+
+ return res;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java
new file mode 100644
index 0000000..fcce4a5
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableLazyValue.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+/**
+ *
+ */
+interface PortableLazyValue extends PortableBuilderSerializationAware {
+ /**
+ * @return Value.
+ */
+ public Object value();
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java
new file mode 100644
index 0000000..30978ad
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataCollector.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+
+import java.lang.reflect.*;
+import java.math.*;
+import java.sql.*;
+import java.util.*;
+import java.util.Date;
+
+/**
+ * Writer for meta data collection.
+ */
+class PortableMetaDataCollector implements PortableWriter {
+ /** */
+ private final Map<String, String> meta = new HashMap<>();
+
+ /** */
+ private final String typeName;
+
+ /**
+ * @param typeName Type name.
+ */
+ PortableMetaDataCollector(String typeName) {
+ this.typeName = typeName;
+ }
+
+ /**
+ * @return Field meta data.
+ */
+ Map<String, String> meta() {
+ return meta;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeByte(String fieldName, byte val) throws PortableException {
+ add(fieldName, byte.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeShort(String fieldName, short val) throws PortableException {
+ add(fieldName, short.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeInt(String fieldName, int val) throws PortableException {
+ add(fieldName, int.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeLong(String fieldName, long val) throws PortableException {
+ add(fieldName, long.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeFloat(String fieldName, float val) throws PortableException {
+ add(fieldName, float.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeDouble(String fieldName, double val) throws PortableException {
+ add(fieldName, double.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeChar(String fieldName, char val) throws PortableException {
+ add(fieldName, char.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBoolean(String fieldName, boolean val) throws PortableException {
+ add(fieldName, boolean.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeDecimal(String fieldName, @Nullable BigDecimal val) throws PortableException {
+ add(fieldName, PortableClassDescriptor.Mode.DECIMAL.typeName());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeString(String fieldName, @Nullable String val) throws PortableException {
+ add(fieldName, String.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeUuid(String fieldName, @Nullable UUID val) throws PortableException {
+ add(fieldName, UUID.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeDate(String fieldName, @Nullable Date val) throws PortableException {
+ add(fieldName, Date.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTimestamp(String fieldName, @Nullable Timestamp val) throws PortableException {
+ add(fieldName, Timestamp.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <T extends Enum<?>> void writeEnum(String fieldName, T val) throws PortableException {
+ add(fieldName, Enum.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <T extends Enum<?>> void writeEnumArray(String fieldName, T[] val) throws PortableException {
+ add(fieldName, Enum[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeObject(String fieldName, @Nullable Object obj) throws PortableException {
+ add(fieldName, Object.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeByteArray(String fieldName, @Nullable byte[] val) throws PortableException {
+ add(fieldName, byte[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeShortArray(String fieldName, @Nullable short[] val) throws PortableException {
+ add(fieldName, short[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeIntArray(String fieldName, @Nullable int[] val) throws PortableException {
+ add(fieldName, int[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeLongArray(String fieldName, @Nullable long[] val) throws PortableException {
+ add(fieldName, long[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeFloatArray(String fieldName, @Nullable float[] val) throws PortableException {
+ add(fieldName, float[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeDoubleArray(String fieldName, @Nullable double[] val) throws PortableException {
+ add(fieldName, double[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeCharArray(String fieldName, @Nullable char[] val) throws PortableException {
+ add(fieldName, char[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeBooleanArray(String fieldName, @Nullable boolean[] val) throws PortableException {
+ add(fieldName, boolean[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeDecimalArray(String fieldName, @Nullable BigDecimal[] val) throws PortableException {
+ add(fieldName, PortableClassDescriptor.Mode.DECIMAL_ARR.typeName());
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeStringArray(String fieldName, @Nullable String[] val) throws PortableException {
+ add(fieldName, String[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeUuidArray(String fieldName, @Nullable UUID[] val) throws PortableException {
+ add(fieldName, UUID[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeDateArray(String fieldName, @Nullable Date[] val) throws PortableException {
+ add(fieldName, Date[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeObjectArray(String fieldName, @Nullable Object[] val) throws PortableException {
+ add(fieldName, Object[].class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <T> void writeCollection(String fieldName, @Nullable Collection<T> col)
+ throws PortableException {
+ add(fieldName, Collection.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public <K, V> void writeMap(String fieldName, @Nullable Map<K, V> map) throws PortableException {
+ add(fieldName, Map.class);
+ }
+
+ /** {@inheritDoc} */
+ @Override public PortableRawWriter rawWriter() {
+ return (PortableRawWriter)Proxy.newProxyInstance(getClass().getClassLoader(),
+ new Class<?>[] { PortableRawWriterEx.class },
+ new InvocationHandler() {
+ @Override public Object invoke(Object proxy, Method mtd, Object[] args) throws Throwable {
+ return null;
+ }
+ });
+ }
+
+ /**
+ * @param name Field name.
+ * @param fieldType Field type.
+ * @throws PortableException In case of error.
+ */
+ private void add(String name, Class<?> fieldType) throws PortableException {
+ assert fieldType != null;
+
+ add(name, fieldType.getSimpleName());
+ }
+
+ /**
+ * @param name Field name.
+ * @param fieldTypeName Field type name.
+ * @throws PortableException In case of error.
+ */
+ private void add(String name, String fieldTypeName) throws PortableException {
+ assert name != null;
+
+ String oldFieldTypeName = meta.put(name, fieldTypeName);
+
+ if (oldFieldTypeName != null && !oldFieldTypeName.equals(fieldTypeName)) {
+ throw new PortableException(
+ "Field is written twice with different types [" +
+ "typeName=" + typeName +
+ ", fieldName=" + name +
+ ", fieldTypeName1=" + oldFieldTypeName +
+ ", fieldTypeName2=" + fieldTypeName +
+ ']'
+ );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java
new file mode 100644
index 0000000..e8154ab
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataHandler.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.portable.*;
+
+/**
+ * Portable meta data handler.
+ */
+public interface PortableMetaDataHandler {
+ /**
+ * Adds meta data.
+ *
+ * @param typeId Type ID.
+ * @param meta Meta data.
+ * @throws PortableException In case of error.
+ */
+ public void addMeta(int typeId, PortableMetadata meta) throws PortableException;
+
+ /**
+ * Gets meta data for provided type ID.
+ *
+ * @param typeId Type ID.
+ * @return Meta data.
+ * @throws PortableException In case of error.
+ */
+ public PortableMetadata metadata(int typeId) throws PortableException;
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java
new file mode 100644
index 0000000..697a981
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableMetaDataImpl.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.tostring.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Portable meta data implementation.
+ */
+public class PortableMetaDataImpl implements PortableMetadata, PortableMarshalAware, Externalizable {
+ /** */
+ private static final long serialVersionUID = 0L;
+
+ /** */
+ private String typeName;
+
+ /** */
+ @GridToStringInclude
+ private Map<String, String> fields;
+
+ /** */
+ private volatile Map<Integer, String> fldIdToName;
+
+ /** */
+ private String affKeyFieldName;
+
+ /**
+ * For {@link Externalizable}.
+ */
+ public PortableMetaDataImpl() {
+ // No-op.
+ }
+
+ /**
+ * @param typeName Type name.
+ * @param fields Fields map.
+ * @param affKeyFieldName Affinity key field name.
+ */
+ public PortableMetaDataImpl(String typeName, @Nullable Map<String, String> fields,
+ @Nullable String affKeyFieldName) {
+ assert typeName != null;
+
+ this.typeName = typeName;
+ this.fields = fields;
+ this.affKeyFieldName = affKeyFieldName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public String typeName() {
+ return typeName;
+ }
+
+ /** {@inheritDoc} */
+ @Override public Collection<String> fields() {
+ return fields != null ? fields.keySet() : Collections.<String>emptyList();
+ }
+
+ /**
+ * @return Fields.
+ */
+ public Map<String, String> fields0() {
+ return fields != null ? fields : Collections.<String, String>emptyMap();
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public String fieldTypeName(String fieldName) {
+ return fields != null ? fields.get(fieldName) : null;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override public String affinityKeyFieldName() {
+ return affKeyFieldName;
+ }
+
+ /**
+ * @return Fields meta data.
+ */
+ public Map<String, String> fieldsMeta() {
+ return fields != null ? fields : Collections.<String, String>emptyMap();
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeExternal(ObjectOutput out) throws IOException {
+ U.writeString(out, typeName);
+ U.writeMap(out, fields);
+ U.writeString(out, affKeyFieldName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ typeName = U.readString(in);
+ fields = U.readMap(in);
+ affKeyFieldName = U.readString(in);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writePortable(PortableWriter writer) throws PortableException {
+ PortableRawWriter raw = writer.rawWriter();
+
+ raw.writeString(typeName);
+ raw.writeString(affKeyFieldName);
+ raw.writeMap(fields);
+ }
+
+ /** {@inheritDoc} */
+ @Override public void readPortable(PortableReader reader) throws PortableException {
+ PortableRawReader raw = reader.rawReader();
+
+ typeName = raw.readString();
+ affKeyFieldName = raw.readString();
+ fields = raw.readMap();
+ }
+
+ /** {@inheritDoc} */
+ @Override public String toString() {
+ return S.toString(PortableMetaDataImpl.class, this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/878dcd92/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java
new file mode 100644
index 0000000..16038d9
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableObjectArrayLazyValue.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.portable;
+
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.apache.ignite.portable.*;
+
+/**
+ *
+ */
+class PortableObjectArrayLazyValue extends PortableAbstractLazyValue {
+ /** */
+ private Object[] lazyValsArr;
+
+ /** */
+ private int compTypeId;
+
+ /** */
+ private String clsName;
+
+ /**
+ * @param reader Reader.
+ */
+ protected PortableObjectArrayLazyValue(PortableBuilderReader reader) {
+ super(reader, reader.position() - 1);
+
+ int typeId = reader.readInt();
+
+ if (typeId == GridPortableMarshaller.UNREGISTERED_TYPE_ID) {
+ clsName = reader.readString();
+
+ Class cls;
+
+ try {
+ // TODO: IGNITE-1272 - Is class loader needed here?
+ cls = U.forName(reader.readString(), null);
+ }
+ catch (ClassNotFoundException e) {
+ throw new PortableInvalidClassException("Failed to load the class: " + clsName, e);
+ }
+
+ compTypeId = reader.portableContext().descriptorForClass(cls).typeId();
+ }
+ else {
+ compTypeId = typeId;
+ clsName = null;
+ }
+
+ int size = reader.readInt();
+
+ lazyValsArr = new Object[size];
+
+ for (int i = 0; i < size; i++)
+ lazyValsArr[i] = reader.parseValue();
+ }
+
+ /** {@inheritDoc} */
+ @Override protected Object init() {
+ for (int i = 0; i < lazyValsArr.length; i++) {
+ if (lazyValsArr[i] instanceof PortableLazyValue)
+ lazyValsArr[i] = ((PortableLazyValue)lazyValsArr[i]).value();
+ }
+
+ return lazyValsArr;
+ }
+
+ /** {@inheritDoc} */
+ @Override public void writeTo(PortableWriterExImpl writer, PortableBuilderSerializer ctx) {
+ if (clsName == null)
+ ctx.writeArray(writer, GridPortableMarshaller.OBJ_ARR, lazyValsArr, compTypeId);
+ else
+ ctx.writeArray(writer, GridPortableMarshaller.OBJ_ARR, lazyValsArr, clsName);
+ }
+}