jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r1444166 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment: SegmentBlob.java SegmentNodeState.java SegmentPropertyState.java SegmentStream.java SegmentWriter.java
Date Fri, 08 Feb 2013 17:54:51 GMT
Author: jukka
Date: Fri Feb  8 17:54:50 2013
New Revision: 1444166

URL: http://svn.apache.org/r1444166
Log:
OAK-593: Segment-based MK

Initial segment-based node and property state implementations.

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentPropertyState.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentStream.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java?rev=1444166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentBlob.java
Fri Feb  8 17:54:50 2013
@@ -0,0 +1,78 @@
+/*
+ * 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.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.Blob;
+
+public class SegmentBlob implements Blob {
+
+    private final SegmentReader reader;
+
+    private final RecordId recordId;
+
+    SegmentBlob(SegmentReader reader, RecordId recordId) {
+        this.reader = checkNotNull(reader);
+        this.recordId = checkNotNull(recordId);
+    }
+
+    @Override @Nonnull
+    public SegmentStream getNewStream() {
+        return reader.readStream(recordId);
+    }
+
+    @Override
+    public long length() {
+        SegmentStream stream = getNewStream();
+        try {
+            return stream.getLength();
+        } finally {
+            stream.close();
+        }
+    }
+
+    @Override
+    public byte[] sha256() {
+        SegmentStream stream = getNewStream();
+        try {
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+
+            byte[] buffer = new byte[1024];
+            for (int n = stream.read(buffer); n != -1; n = stream.read(buffer)) {
+                digest.update(buffer, 0, n);
+            }
+
+            return digest.digest();
+        } catch (NoSuchAlgorithmException e) {
+            throw new UnsupportedOperationException("SHA256 not supported", e);
+        } finally {
+            stream.close();
+        }
+    }
+
+    @Override
+    public int compareTo(Blob that) {
+        throw new UnsupportedOperationException();
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java?rev=1444166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeState.java
Fri Feb  8 17:54:50 2013
@@ -0,0 +1,114 @@
+/*
+ * 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.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateDiff;
+
+class SegmentNodeState implements NodeState {
+
+    private final SegmentReader reader;
+
+    private final MapRecord properties;
+
+    private final MapRecord childNodes;
+
+    SegmentNodeState(SegmentReader reader, RecordId id) {
+        this.reader = checkNotNull(reader);
+
+        checkNotNull(id);
+        this.properties = new MapRecord(reader.readRecordId(id, 0));
+        this.childNodes = new MapRecord(reader.readRecordId(id, 4));
+    }
+
+    @Override
+    public long getPropertyCount() {
+        return properties.size(reader);
+    }
+
+    @Override @CheckForNull
+    public PropertyState getProperty(String name) {
+        checkNotNull(name);
+        RecordId propertyId = properties.getEntry(reader, name);
+        if (propertyId != null) {
+            return new SegmentPropertyState(reader, name, propertyId);
+        } else {
+            return null;
+        }
+    }
+
+    @Override @Nonnull
+    public Iterable<? extends PropertyState> getProperties() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public long getChildNodeCount() {
+        return childNodes.size(reader);
+    }
+
+    @Override
+    public boolean hasChildNode(String name) {
+        checkNotNull(name);
+        return childNodes.getEntry(reader, name) != null;
+    }
+
+    @Override @CheckForNull
+    public NodeState getChildNode(String name) {
+        checkNotNull(name);
+        RecordId childNodeId = childNodes.getEntry(reader, name);
+        if (childNodeId != null) {
+            return new SegmentNodeState(reader, childNodeId);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Iterable<String> getChildNodeNames() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override @Nonnull
+    public Iterable<? extends ChildNodeEntry> getChildNodeEntries() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override @Nonnull
+    public NodeBuilder builder() {
+        return new MemoryNodeBuilder(this);
+    }
+
+    @Override
+    public void compareAgainstBaseState(NodeState base, NodeStateDiff diff) {
+        // TODO Auto-generated method stub
+        
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentPropertyState.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentPropertyState.java?rev=1444166&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentPropertyState.java
(added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentPropertyState.java
Fri Feb  8 17:54:50 2013
@@ -0,0 +1,158 @@
+/*
+ * 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.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import javax.annotation.Nonnull;
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.value.Conversions;
+
+class SegmentPropertyState implements PropertyState {
+
+    private final SegmentReader reader;
+
+    private final String name;
+
+    private final int tag;
+
+    private final int count;
+
+    private final ListRecord values;
+
+    SegmentPropertyState(SegmentReader reader, String name, RecordId id) {
+        this.reader = checkNotNull(reader);
+        this.name = checkNotNull(name);
+
+        checkNotNull(id);
+        this.tag = reader.readInt(id, 0);
+        this.count = reader.readInt(id, 4);
+        this.values = new ListRecord(reader.readRecordId(id, 8), count());
+    }
+
+    @Override @Nonnull
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isArray() {
+        return count != -1;
+    }
+
+    @Override
+    public int count() {
+        if (isArray()) {
+            return count;
+        } else {
+            return 1;
+        }
+    }
+
+    @Override
+    public Type<?> getType() {
+        return Type.fromTag(tag, isArray());
+    }
+
+    @Override @Nonnull @SuppressWarnings("unchecked")
+    public <T> T getValue(Type<T> type) {
+        if (type.isArray()) {
+            final Type<?> base = type.getBaseType();
+            return (T) new Iterable<Object>() {
+                @Override
+                public Iterator<Object> iterator() {
+                    return new Iterator<Object>() {
+                        private int index = 0;
+                        @Override
+                        public boolean hasNext() {
+                            return index < count();
+                        }
+                        @Override
+                        public Object next() {
+                            if (hasNext()) {
+                                return getValue(base, index++);
+                            } else {
+                                throw new NoSuchElementException();
+                            }
+                        }
+                        @Override
+                        public void remove() {
+                            throw new UnsupportedOperationException();
+                        }
+                    };
+                }
+            };
+        } else {
+            return getValue(type, 0);
+        }
+    }
+
+    @Override
+    public long size() {
+        return size(0);
+    }
+
+    @Override @Nonnull @SuppressWarnings("unchecked")
+    public <T> T getValue(Type<T> type, int index) {
+        checkNotNull(type);
+        checkArgument(!type.isArray(), "Type must not be an array type");
+
+        RecordId valueId = values.getEntry(reader, index);
+        if (type == Type.BINARY) {
+            return (T) new SegmentBlob(reader, valueId);
+        } else {
+            String value = reader.readString(valueId);
+            switch (type.tag()) {
+            case PropertyType.BOOLEAN:
+                return (T) Boolean.valueOf(Conversions.convert(value).toBoolean());
+            case PropertyType.DATE:
+                return (T) Conversions.convert(value).toDate();
+            case PropertyType.DECIMAL:
+                return (T) Conversions.convert(value).toDecimal();
+            case PropertyType.DOUBLE:
+                return (T) Double.valueOf(Conversions.convert(value).toDouble());
+            case PropertyType.LONG:
+                return (T) Long.valueOf(Conversions.convert(value).toLong());
+            case PropertyType.NAME:
+            case PropertyType.PATH:
+            case PropertyType.REFERENCE:
+            case PropertyType.STRING:
+            case PropertyType.URI:
+            case PropertyType.WEAKREFERENCE:
+                return (T) value;
+            case PropertyType.UNDEFINED:
+                throw new IllegalArgumentException("Undefined type");
+            default:
+                throw new UnsupportedOperationException("Unknown type: " + type);
+            }
+        }
+    }
+
+    @Override
+    public long size(int index) {
+        RecordId valueId = values.getEntry(reader, index);
+        return reader.readLong(valueId, 0);
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentStream.java?rev=1444166&r1=1444165&r2=1444166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentStream.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentStream.java
Fri Feb  8 17:54:50 2013
@@ -1,116 +1,120 @@
-/*
- * 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.jackrabbit.oak.plugins.segment;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkPositionIndexes;
-import static org.apache.jackrabbit.oak.plugins.segment.SegmentWriter.BLOCK_SIZE;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class SegmentStream extends InputStream {
-
-    private final SegmentReader reader;
-
-    private final ListRecord blocks;
-
-    private final long length;
-
-    private long position = 0;
-
-    private long mark = 0;
-
-    SegmentStream(SegmentReader reader, ListRecord blocks, long length) {
-        this.reader = reader;
-        this.blocks = blocks;
-        this.length = length;
-    }
-
-    public long getLength() {
-        return length;
-    }
-
-    @Override
-    public boolean markSupported() {
-        return true;
-    }
-
-    @Override
-    public synchronized void mark(int readlimit) {
-        mark = position;
-    }
-
-    @Override
-    public synchronized void reset() {
-        position = mark;
-    }
-
-    @Override
-    public int read() throws IOException {
-        byte[] b = new byte[1];
-        if (read(b) != -1) {
-            return b[0] & 0xff;
-        } else {
-            return -1;
-        }
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        checkNotNull(b);
-        checkPositionIndexes(off, off + len, b.length);
-        if (len == 0) {
-            return 0;
-        } else if (position == length) {
-            return -1;
-        } else {
-            int blockIndex = (int) (position / SegmentWriter.BLOCK_SIZE);
-            int blockOffset = (int) (position % SegmentWriter.BLOCK_SIZE);
-
-            if (blockOffset + len > SegmentWriter.BLOCK_SIZE) {
-                len = SegmentWriter.BLOCK_SIZE - blockOffset;
-            }
-            if (position + len > length) {
-                len = (int) (length - position);
-            }
-
-            BlockRecord block = reader.readBlock(
-                    blocks.getEntry(reader, blockIndex), BLOCK_SIZE);
-            len = block.read(reader, blockOffset, b, off, len);
-            position += len;
-            return len;
-        }
-    }
-
-    @Override
-    public long skip(long n) {
-        if (position + n > length) {
-            n = length - position;
-        } else if (position + n < 0) {
-            n = -position;
-        }
-        position += n;
-        return n;
-    }
-
-    @Override
-    public void close() {
-        position = length;
-    }
-
-}
+/*
+ * 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.jackrabbit.oak.plugins.segment;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkPositionIndexes;
+import static org.apache.jackrabbit.oak.plugins.segment.SegmentWriter.BLOCK_SIZE;
+
+import java.io.InputStream;
+
+public class SegmentStream extends InputStream {
+
+    private final SegmentReader reader;
+
+    private final ListRecord blocks;
+
+    private final long length;
+
+    private long position = 0;
+
+    private long mark = 0;
+
+    SegmentStream(SegmentReader reader, ListRecord blocks, long length) {
+        this.reader = reader;
+        this.blocks = blocks;
+        this.length = length;
+    }
+
+    public long getLength() {
+        return length;
+    }
+
+    @Override
+    public boolean markSupported() {
+        return true;
+    }
+
+    @Override
+    public synchronized void mark(int readlimit) {
+        mark = position;
+    }
+
+    @Override
+    public synchronized void reset() {
+        position = mark;
+    }
+
+    @Override
+    public int read() {
+        byte[] b = new byte[1];
+        if (read(b) != -1) {
+            return b[0] & 0xff;
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public int read(byte[] b) {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) {
+        checkNotNull(b);
+        checkPositionIndexes(off, off + len, b.length);
+        if (len == 0) {
+            return 0;
+        } else if (position == length) {
+            return -1;
+        } else {
+            int blockIndex = (int) (position / SegmentWriter.BLOCK_SIZE);
+            int blockOffset = (int) (position % SegmentWriter.BLOCK_SIZE);
+
+            if (blockOffset + len > SegmentWriter.BLOCK_SIZE) {
+                len = SegmentWriter.BLOCK_SIZE - blockOffset;
+            }
+            if (position + len > length) {
+                len = (int) (length - position);
+            }
+
+            BlockRecord block = reader.readBlock(
+                    blocks.getEntry(reader, blockIndex), BLOCK_SIZE);
+            len = block.read(reader, blockOffset, b, off, len);
+            position += len;
+            return len;
+        }
+    }
+
+    @Override
+    public long skip(long n) {
+        if (position + n > length) {
+            n = length - position;
+        } else if (position + n < 0) {
+            n = -position;
+        }
+        position += n;
+        return n;
+    }
+
+    @Override
+    public void close() {
+        position = length;
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java?rev=1444166&r1=1444165&r2=1444166&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
(original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentWriter.java
Fri Feb  8 17:54:50 2013
@@ -31,8 +31,17 @@ import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
+import javax.jcr.PropertyType;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+
 import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.io.ByteStreams;
 
 public class SegmentWriter {
@@ -357,4 +366,53 @@ public class SegmentWriter {
         }
     }
 
+    private RecordId writeProperty(PropertyState state) {
+        Type<?> type = state.getType();
+        int count = state.count();
+
+        List<RecordId> valueIds = Lists.newArrayList();
+        for (int i = 0; i < count; i++) {
+            if (type.tag() == PropertyType.BINARY) {
+                try {
+                    valueIds.add(writeStream(
+                            state.getValue(Type.BINARY, i).getNewStream()));
+                } catch (IOException e) {
+                    throw new IllegalStateException("Unexpected IOException", e);
+                }
+            } else {
+                valueIds.add(writeString(state.getValue(Type.STRING, i)));
+            }
+        }
+        RecordId valueId = writeList(valueIds);
+
+        RecordId propertyId = prepare(8, Collections.singleton(valueId));
+        buffer.putInt(type.tag());
+        if (state.isArray()) {
+            buffer.putInt(count);
+        } else {
+            buffer.putInt(-1);
+        }
+        writeRecordId(valueId);
+        return propertyId;
+    }
+
+    public RecordId writeNode(NodeState state) {
+        Map<String, RecordId> childNodes = Maps.newHashMap();
+        for (ChildNodeEntry entry : state.getChildNodeEntries()) {
+            childNodes.put(entry.getName(), writeNode(entry.getNodeState()));
+        }
+        RecordId childNodesId = writeMap(childNodes);
+
+        Map<String, RecordId> properties = Maps.newHashMap();
+        for (PropertyState property : state.getProperties()) {
+            properties.put(property.getName(), writeProperty(property));
+        }
+        RecordId propertiesId = writeMap(properties);
+
+        RecordId id = prepare(0, ImmutableList.of(propertiesId, childNodesId));
+        writeRecordId(propertiesId);
+        writeRecordId(childNodesId);
+        return id;
+    }
+
 }



Mime
View raw message