Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/FailedRequestListener.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/FailedRequestListener.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/FailedRequestListener.java (working copy) @@ -1,42 +0,0 @@ -/* - * 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.segment.standby.client; - -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.util.concurrent.Promise; - -public class FailedRequestListener implements ChannelFutureListener { - - private final Promise promise; - - public FailedRequestListener(Promise promise) { - this.promise = promise; - } - - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - promise.setFailure(future.cause()); - future.channel().close(); - } else { - future.channel().read(); - } - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetBlobResponseHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetBlobResponseHandler.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetBlobResponseHandler.java (working copy) @@ -0,0 +1,39 @@ +/* + * 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.segment.standby.client; + +import java.util.concurrent.BlockingQueue; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobResponse; + +class GetBlobResponseHandler extends SimpleChannelInboundHandler { + + private final BlockingQueue queue; + + GetBlobResponseHandler(BlockingQueue queue) { + this.queue = queue; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, GetBlobResponse msg) throws Exception { + queue.offer(msg); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetBlobResponseHandler.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetHeadResponseHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetHeadResponseHandler.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetHeadResponseHandler.java (working copy) @@ -0,0 +1,39 @@ +/* + * 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.segment.standby.client; + +import java.util.concurrent.BlockingQueue; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponse; + +class GetHeadResponseHandler extends SimpleChannelInboundHandler { + + private final BlockingQueue queue; + + GetHeadResponseHandler(BlockingQueue queue) { + this.queue = queue; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, GetHeadResponse msg) throws Exception { + queue.offer(msg); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetHeadResponseHandler.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetSegmentResponseHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetSegmentResponseHandler.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetSegmentResponseHandler.java (working copy) @@ -0,0 +1,39 @@ +/* + * 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.segment.standby.client; + +import java.util.concurrent.BlockingQueue; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponse; + +class GetSegmentResponseHandler extends SimpleChannelInboundHandler { + + private final BlockingQueue queue; + + GetSegmentResponseHandler(BlockingQueue queue) { + this.queue = queue; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, GetSegmentResponse msg) throws Exception { + queue.offer(msg); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/GetSegmentResponseHandler.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/SegmentLoaderHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/SegmentLoaderHandler.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/SegmentLoaderHandler.java (working copy) @@ -1,258 +0,0 @@ -/* - * 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.segment.standby.client; - -import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount; -import static org.apache.jackrabbit.oak.segment.standby.codec.Messages.newGetBlobRequest; -import static org.apache.jackrabbit.oak.segment.standby.codec.Messages.newGetSegmentRequest; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import org.apache.jackrabbit.oak.api.Blob; -import org.apache.jackrabbit.oak.segment.RecordId; -import org.apache.jackrabbit.oak.segment.Segment; -import org.apache.jackrabbit.oak.segment.SegmentNodeBuilder; -import org.apache.jackrabbit.oak.segment.SegmentNodeState; -import org.apache.jackrabbit.oak.segment.SegmentNotFoundException; -import org.apache.jackrabbit.oak.segment.standby.codec.SegmentReply; -import org.apache.jackrabbit.oak.segment.standby.store.RemoteSegmentLoader; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SegmentLoaderHandler extends ChannelInboundHandlerAdapter implements RemoteSegmentLoader { - - private static final Logger log = LoggerFactory.getLogger(SegmentLoaderHandler.class); - - private final StandbyStore store; - private final String clientID; - private final RecordId head; - private final AtomicBoolean running; - private final int readTimeoutMs; - private final boolean autoClean; - - private volatile ChannelHandlerContext ctx; - - private final BlockingQueue segment = new LinkedBlockingQueue(); - - // Use a separate thread for sync'ing. Leave the I/O thread free to process - // I/O requests. - private ExecutorService syncExecutor; - - public SegmentLoaderHandler(StandbyStore store, RecordId head, String clientID, AtomicBoolean running, int readTimeoutMs, boolean autoClean) { - this.store = store; - this.head = head; - this.clientID = clientID; - this.running = running; - this.readTimeoutMs = readTimeoutMs; - this.autoClean = autoClean; - this.syncExecutor = Executors.newSingleThreadExecutor(); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - log.error("Exception caught, closing channel.", cause); - close(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - syncExecutor.shutdown(); - syncExecutor = null; - } - - @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - if (evt instanceof String) { - onCommand((String) evt); - } - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof SegmentReply) { - onSegmentReply((SegmentReply) msg); - } else { - ctx.fireChannelRead(msg); - } - } - - private void onSegmentReply(SegmentReply reply) { - // Offer the reply from the I/O thread, unblocking the sync thread. - segment.offer(reply); - } - - private void onCommand(String command) { - if (command.equals("sync")) { - syncExecutor.submit(new Runnable() { - - @Override - public void run() { - sync(); - } - - }); - } - } - - private void sync() { - log.debug("new head id " + head); - long t = System.currentTimeMillis(); - long preSyncSize = -1; - if (autoClean) { - preSyncSize = store.size(); - } - - try { - store.preSync(this); - SegmentNodeState before = store.getHead(); - SegmentNodeBuilder builder = before.builder(); - - SegmentNodeState current = store.newSegmentNodeState(head); - do { - try { - current.compareAgainstBaseState(before, - new StandbyApplyDiff(builder, store, this)); - break; - } catch (SegmentNotFoundException e) { - // the segment is locally damaged or not present anymore - // lets try to read this from the primary again - String id = e.getSegmentId(); - Segment s = readSegment(e.getSegmentId()); - if (s == null) { - log.warn("can't read locally corrupt segment " + id + " from primary"); - throw e; - } - - log.debug("did reread locally corrupt segment " + id + " with size " + s.size()); - store.persist(s.getSegmentId(), s); - } - } while(true); - boolean ok = store.setHead(before, builder.getNodeState()); - log.debug("updated head state successfully: {} in {}ms.", ok, - System.currentTimeMillis() - t); - - if (autoClean && preSyncSize > 0) { - long postSyncSize = store.size(); - // if size gain is over 25% call cleanup - if (postSyncSize - preSyncSize > 0.25 * preSyncSize) { - log.info( - "Store size increased from {} to {}, will run cleanup.", - humanReadableByteCount(preSyncSize), - humanReadableByteCount(postSyncSize)); - store.cleanup(); - } - } - } finally { - store.postSync(); - close(); - } - } - - @Override - public Segment readSegment(final String id) { - // Use the I/O thread to write the request to the server - ctx.writeAndFlush(newGetSegmentRequest(this.clientID, id)); - // Wait on the sync thread for the response. - return getSegment(id); - } - - @Override - public Blob readBlob(String blobId) { - // Use the I/O thread to write the request to the server - ctx.writeAndFlush(newGetBlobRequest(this.clientID, blobId)); - // Wait on the sync thread for the response. - return getBlob(blobId); - } - - private Segment getSegment(final String id) { - return getReply(id, SegmentReply.SEGMENT).getSegment(); - } - - private Blob getBlob(final String id) { - return getReply(id, SegmentReply.BLOB).getBlob(); - } - - private SegmentReply getReply(final String id, int type) { - boolean interrupted = false; - try { - for (;;) { - try { - // Block the sync thread for a response from the server. - SegmentReply r = segment.poll(readTimeoutMs, TimeUnit.MILLISECONDS); - - if (r == null) { - log.warn("timeout waiting for {}", id); - return SegmentReply.empty(); - } - - if (r.getType() == type) { - switch (r.getType()) { - case SegmentReply.SEGMENT: - if (r.getSegment().getSegmentId().toString() - .equals(id)) { - return r; - } - break; - case SegmentReply.BLOB: - if (r.getBlob().getBlobId().equals(id)) { - return r; - } - break; - } - } - } catch (InterruptedException ignore) { - interrupted = true; - } - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - - @Override - public void close() { - ctx.close(); - } - - @Override - public boolean isClosed() { - return !ctx.channel().isActive(); - } - - @Override - public boolean isRunning() { - return running.get(); - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyApplyDiff.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyApplyDiff.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyApplyDiff.java (working copy) @@ -1,214 +0,0 @@ -/* - * 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.segment.standby.client; - -import static org.apache.jackrabbit.oak.api.Type.BINARIES; -import static org.apache.jackrabbit.oak.api.Type.BINARY; - -import java.io.IOException; - -import org.apache.jackrabbit.oak.api.Blob; -import org.apache.jackrabbit.oak.api.PropertyState; -import org.apache.jackrabbit.oak.api.Type; -import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; -import org.apache.jackrabbit.oak.segment.RecordId; -import org.apache.jackrabbit.oak.segment.SegmentBlob; -import org.apache.jackrabbit.oak.segment.SegmentNodeState; -import org.apache.jackrabbit.oak.segment.standby.store.RemoteSegmentLoader; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; -import org.apache.jackrabbit.oak.spi.state.NodeBuilder; -import org.apache.jackrabbit.oak.spi.state.NodeState; -import org.apache.jackrabbit.oak.spi.state.NodeStateDiff; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class StandbyApplyDiff implements NodeStateDiff { - - private static final Logger log = LoggerFactory - .getLogger(StandbyApplyDiff.class); - - private final NodeBuilder builder; - - private final StandbyStore store; - - private final boolean hasDataStore; - - private final RemoteSegmentLoader loader; - - private final String path; - - /** - * read-only traversal of the diff that has 2 properties: one is to log all - * the content changes, second is to drill down to properly level, so that - * missing binaries can be sync'ed if needed - */ - private final boolean logOnly; - - public StandbyApplyDiff(NodeBuilder builder, StandbyStore store, - RemoteSegmentLoader loader) { - this(builder, store, loader, "/", false); - } - - private StandbyApplyDiff(NodeBuilder builder, StandbyStore store, - RemoteSegmentLoader loader, String path, boolean logOnly) { - this.builder = builder; - this.store = store; - this.hasDataStore = store.getBlobStore() != null; - this.loader = loader; - this.path = path; - this.logOnly = logOnly; - } - - @Override - public boolean propertyAdded(PropertyState after) { - if (!loader.isRunning()) { - return false; - } - if (!logOnly) { - builder.setProperty(binaryCheck(after)); - } else { - binaryCheck(after); - } - return true; - } - - @Override - public boolean propertyChanged(PropertyState before, PropertyState after) { - if (!loader.isRunning()) { - return false; - } - if (!logOnly) { - builder.setProperty(binaryCheck(after)); - } else { - binaryCheck(after); - } - return true; - } - - @Override - public boolean propertyDeleted(PropertyState before) { - if (!loader.isRunning()) { - return false; - } - if (!logOnly) { - builder.removeProperty(before.getName()); - } - return true; - } - - private PropertyState binaryCheck(PropertyState property) { - Type type = property.getType(); - if (type == BINARY) { - binaryCheck(property.getValue(Type.BINARY), property.getName()); - } else if (type == BINARIES) { - for (Blob blob : property.getValue(BINARIES)) { - binaryCheck(blob, property.getName()); - } - } - return property; - } - - private void binaryCheck(Blob b, String pName) { - if (b instanceof SegmentBlob) { - SegmentBlob sb = (SegmentBlob) b; - // verify if the blob exists - if (sb.isExternal() && hasDataStore && b.getReference() == null) { - String blobId = sb.getBlobId(); - if (blobId != null) { - readBlob(blobId, pName); - } - } - } else { - log.warn("Unknown Blob {} at {}, ignoring", b.getClass().getName(), - path + "#" + pName); - } - } - - private void readBlob(String blobId, String pName) { - Blob read = loader.readBlob(blobId); - if (read != null) { - try { - store.getBlobStore().writeBlob(read.getNewStream()); - } catch (IOException f) { - throw new IllegalStateException("Unable to persist blob " - + blobId + " at " + path + "#" + pName, f); - } - } else { - throw new IllegalStateException("Unable to load remote blob " - + blobId + " at " + path + "#" + pName); - } - } - - @Override - public boolean childNodeAdded(String name, NodeState after) { - return process(name, "childNodeAdded", EmptyNodeState.EMPTY_NODE, - after); - } - - @Override - public boolean childNodeChanged(String name, NodeState before, - NodeState after) { - return process(name, "childNodeChanged", before, after); - } - - private boolean process(String name, String op, NodeState before, - NodeState after) { - if (!loader.isRunning()) { - return false; - } - if (after instanceof SegmentNodeState) { - if (log.isTraceEnabled()) { - log.trace("{} {}, readonly binary check {}", op, path + name, - logOnly); - } - if (!logOnly) { - RecordId id = ((SegmentNodeState) after).getRecordId(); - builder.setChildNode(name, store.newSegmentNodeState(id)); - } - if ("checkpoints".equals(name)) { - // if we're on the /checkpoints path, there's no need for a deep - // traversal to verify binaries - return true; - } - if (hasDataStore) { - // has external datastore, we need a deep - // traversal to verify binaries - return after.compareAgainstBaseState(before, - new StandbyApplyDiff(builder.getChildNode(name), store, - loader, path + name + "/", true)); - } else { - return true; - } - } - return false; - } - - @Override - public boolean childNodeDeleted(String name, NodeState before) { - if (!loader.isRunning()) { - return false; - } - log.trace("childNodeDeleted {}, RO:{}", path + name, logOnly); - if (!logOnly) { - builder.getChildNode(name).remove(); - } - return true; - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java (working copy) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClient.java (working copy) @@ -1,269 +1,180 @@ /* - * 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 + * 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 + * 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. + * 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.segment.standby.client; -import java.io.Closeable; -import java.lang.management.ManagementFactory; -import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.StandardMBean; -import javax.net.ssl.SSLException; - import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelFuture; +import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.compression.SnappyFramedDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.CharsetUtil; -import org.apache.jackrabbit.oak.segment.SegmentStore; -import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.codec.RecordIdDecoder; -import org.apache.jackrabbit.oak.segment.standby.jmx.ClientStandbyStatusMBean; -import org.apache.jackrabbit.oak.segment.standby.jmx.StandbyStatusMBean; -import org.apache.jackrabbit.oak.segment.standby.store.CommunicationObserver; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobRequest; +import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobRequestEncoder; +import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobResponse; +import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadRequest; +import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadRequestEncoder; +import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponse; +import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequest; +import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequestEncoder; +import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponse; +import org.apache.jackrabbit.oak.segment.standby.codec.ResponseDecoder; -public final class StandbyClient implements ClientStandbyStatusMBean, Runnable, Closeable { - public static final String CLIENT_ID_PROPERTY_NAME = "standbyID"; +class StandbyClient implements AutoCloseable { - private static final Logger log = LoggerFactory - .getLogger(StandbyClient.class); + private final BlockingQueue headQueue = new LinkedBlockingDeque<>(); - private final String host; - private final int port; + private final BlockingQueue segmentQueue = new LinkedBlockingDeque<>(); + + private final BlockingQueue blobQueue = new LinkedBlockingDeque<>(); + + private final boolean secure; + private final int readTimeoutMs; - private final boolean autoClean; - private final StandbyStore store; - private final CommunicationObserver observer; - private StandbyClientHandler handler; - private EventLoopGroup group; - private SslContext sslContext; - private boolean active = false; - private int failedRequests; - private long lastSuccessfulRequest; - private volatile String state; - private final Object sync = new Object(); + private String clientId; - private final AtomicBoolean running = new AtomicBoolean(true); + private NioEventLoopGroup group; - private long syncStartTimestamp; - private long syncEndTimestamp; + private Channel channel; - public StandbyClient(String host, int port, FileStore store, - boolean secure, int readTimeoutMs, boolean autoClean) - throws SSLException { - this.state = STATUS_INITIALIZING; - this.lastSuccessfulRequest = -1; - this.syncStartTimestamp = -1; - this.syncEndTimestamp = -1; - this.failedRequests = 0; - this.host = host; - this.port = port; - if (secure) { - this.sslContext = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); - } + StandbyClient(String clientId, boolean secure, int readTimeoutMs) { + this.clientId = clientId; + this.secure = secure; this.readTimeoutMs = readTimeoutMs; - this.autoClean = autoClean; - this.store = new StandbyStore(store); - String s = System.getProperty(CLIENT_ID_PROPERTY_NAME); - this.observer = new CommunicationObserver((s == null || s.length() == 0) ? UUID.randomUUID().toString() : s); - - final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); - try { - jmxServer.registerMBean(new StandardMBean(this, ClientStandbyStatusMBean.class), new ObjectName(this.getMBeanName())); - } - catch (Exception e) { - log.error("can register standby status mbean", e); - } } - public String getMBeanName() { - return StandbyStatusMBean.JMX_NAME + ",id=\"" + this.observer.getID() + "\""; - } + void connect(String host, int port) throws Exception { + group = new NioEventLoopGroup(); - public void close() { - stop(); - state = STATUS_CLOSING; - final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); - try { - jmxServer.unregisterMBean(new ObjectName(this.getMBeanName())); - } - catch (Exception e) { - log.error("can unregister standby status mbean", e); - } - observer.unregister(); - shutdownNetty(); - state = STATUS_CLOSED; - } + final SslContext sslContext; - public void run() { - if (!isRunning()) { - // manually stopped - return; + if (secure) { + sslContext = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); + } else { + sslContext = null; } - Bootstrap b; - synchronized (this.sync) { - if (this.active) { - return; - } - state = STATUS_STARTING; - handler = new StandbyClientHandler(this.store, observer, running, - readTimeoutMs, autoClean); - group = new NioEventLoopGroup(); + Bootstrap b = new Bootstrap() + .group(group) + .channel(NioSocketChannel.class) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, readTimeoutMs) + .option(ChannelOption.TCP_NODELAY, true) + .option(ChannelOption.SO_REUSEADDR, true) + .option(ChannelOption.SO_KEEPALIVE, true) + .handler(new ChannelInitializer() { - b = new Bootstrap(); - b.group(group); - b.channel(NioSocketChannel.class); - b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, readTimeoutMs); - b.option(ChannelOption.TCP_NODELAY, true); - b.option(ChannelOption.SO_REUSEADDR, true); - b.option(ChannelOption.SO_KEEPALIVE, true); + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); - b.handler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslContext != null) { - p.addLast(sslContext.newHandler(ch.alloc())); + if (sslContext != null) { + p.addLast(sslContext.newHandler(ch.alloc())); + } + + p.addLast(new ReadTimeoutHandler(readTimeoutMs, TimeUnit.MILLISECONDS)); + + // Decoders + + p.addLast(new SnappyFramedDecoder(true)); + + // Such a big max frame length is needed because blob + // values are sent in one big message. In future + // versions of the protocol, sending binaries in chunks + // should be considered instead. + + p.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4)); + p.addLast(new ResponseDecoder()); + + // Encoders + + p.addLast(new StringEncoder(CharsetUtil.UTF_8)); + p.addLast(new GetHeadRequestEncoder()); + p.addLast(new GetSegmentRequestEncoder()); + p.addLast(new GetBlobRequestEncoder()); + + // Handlers + + p.addLast(new GetHeadResponseHandler(headQueue)); + p.addLast(new GetSegmentResponseHandler(segmentQueue)); + p.addLast(new GetBlobResponseHandler(blobQueue)); } - p.addLast("readTimeoutHandler", new ReadTimeoutHandler( - readTimeoutMs, TimeUnit.MILLISECONDS)); - p.addLast(new StringEncoder(CharsetUtil.UTF_8)); - p.addLast(new SnappyFramedDecoder(true)); - p.addLast(new RecordIdDecoder(store)); - p.addLast(handler); - } - }); - state = STATUS_RUNNING; - this.active = true; - } - try { - long startTimestamp = System.currentTimeMillis(); - // Start the client. - ChannelFuture f = b.connect(host, port).sync(); - // Wait until the connection is closed. - f.channel().closeFuture().sync(); - this.failedRequests = 0; - this.syncStartTimestamp = startTimestamp; - this.syncEndTimestamp = System.currentTimeMillis(); - this.lastSuccessfulRequest = syncEndTimestamp / 1000; - } catch (Exception e) { - this.failedRequests++; - log.error("Failed synchronizing state.", e); - } finally { - synchronized (this.sync) { - this.active = false; - shutdownNetty(); - } - } - } + }); - private void shutdownNetty() { - if (handler != null) { - handler.close(); - handler = null; - } - if (group != null && !group.isShuttingDown()) { - group.shutdownGracefully(0, 1, TimeUnit.SECONDS).syncUninterruptibly(); - group = null; - } + channel = b.connect(host, port).sync().channel(); } @Override - public String getMode() { - return "client: " + this.observer.getID(); - } + public void close() throws InterruptedException { + channel.close().sync(); + channel = null; - @Override - public boolean isRunning() { - return running.get(); + group.shutdownGracefully(); + group = null; } - @Override - public void start() { - running.set(true); - state = STATUS_RUNNING; - } + String getHead() throws InterruptedException { + channel.writeAndFlush(new GetHeadRequest(clientId)); - @Override - public void stop() { - running.set(false); - state = STATUS_STOPPED; - } + GetHeadResponse response = headQueue.poll(readTimeoutMs, TimeUnit.MILLISECONDS); - @Override - public String getStatus() { - return this.state; - } + if (response == null) { + return null; + } - @Override - public int getFailedRequests() { - return this.failedRequests; + return response.getHeadRecordId(); } - @Override - public int getSecondsSinceLastSuccess() { - if (this.lastSuccessfulRequest < 0) return -1; - return (int)(System.currentTimeMillis() / 1000 - this.lastSuccessfulRequest); - } + byte[] getSegment(String segmentId) throws InterruptedException { + channel.writeAndFlush(new GetSegmentRequest(clientId, segmentId)); - @Override - public int calcFailedRequests() { - return this.getFailedRequests(); - } + GetSegmentResponse response = segmentQueue.poll(readTimeoutMs, TimeUnit.MILLISECONDS); - @Override - public int calcSecondsSinceLastSuccess() { - return this.getSecondsSinceLastSuccess(); - } + if (response == null) { + return null; + } - @Override - public void cleanup() { - store.cleanup(); + return response.getSegmentData(); } - @Override - public long getSyncStartTimestamp() { - return syncStartTimestamp; - } + byte[] getBlob(String blobId) throws InterruptedException { + channel.writeAndFlush(new GetBlobRequest(clientId, blobId)); - @Override - public long getSyncEndTimestamp() { - return syncEndTimestamp; + GetBlobResponse response = blobQueue.poll(readTimeoutMs, TimeUnit.MILLISECONDS); + + if (response == null) { + return null; + } + + return response.getBlobData(); } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientHandler.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyClientHandler.java (working copy) @@ -1,102 +0,0 @@ -/* - * 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.segment.standby.client; - -import static org.apache.jackrabbit.oak.segment.standby.codec.Messages.newGetHeadRequest; - -import java.io.Closeable; -import java.util.concurrent.atomic.AtomicBoolean; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.handler.timeout.ReadTimeoutHandler; -import org.apache.jackrabbit.oak.segment.RecordId; -import org.apache.jackrabbit.oak.segment.standby.codec.RecordIdDecoder; -import org.apache.jackrabbit.oak.segment.standby.codec.ReplyDecoder; -import org.apache.jackrabbit.oak.segment.standby.store.CommunicationObserver; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class StandbyClientHandler extends SimpleChannelInboundHandler implements Closeable { - - private static final Logger log = LoggerFactory - .getLogger(StandbyClientHandler.class); - - private final StandbyStore store; - private final CommunicationObserver observer; - private final AtomicBoolean running; - private final int readTimeoutMs; - private final boolean autoClean; - - public StandbyClientHandler(final StandbyStore store, CommunicationObserver observer, AtomicBoolean running, int readTimeoutMs, boolean autoClean) { - this.store = store; - this.observer = observer; - this.running = running; - this.readTimeoutMs = readTimeoutMs; - this.autoClean = autoClean; - } - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - log.debug("sending head request"); - ctx.writeAndFlush(newGetHeadRequest(this.observer.getID())); - log.debug("did send head request"); - } - - @Override - protected void channelRead0(ChannelHandlerContext ctx, RecordId msg) throws Exception { - setHead(ctx, msg); - }; - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - synchronized void setHead(ChannelHandlerContext ctx, RecordId head) { - if (store.getHead().getRecordId().equals(head)) { - // all sync'ed up - log.debug("no changes on sync."); - return; - } - - log.debug("updating current head to " + head); - ctx.pipeline().remove(ReadTimeoutHandler.class); - ctx.pipeline().remove(RecordIdDecoder.class); - ctx.pipeline().remove(this); - ctx.pipeline().addLast(new ReplyDecoder(store)); - - ctx.pipeline().addLast(new SegmentLoaderHandler(store, head, this.observer.getID(), running, readTimeoutMs, autoClean)); - ctx.pipeline().fireUserEventTriggered("sync"); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - log.error("Exception caught, closing channel.", cause); - close(); - } - - @Override - public void close() { - // This handler doesn't own resources to release - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyDiff.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyDiff.java (working copy) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbyDiff.java (working copy) @@ -22,8 +22,10 @@ import static org.apache.jackrabbit.oak.api.Type.BINARIES; import static org.apache.jackrabbit.oak.api.Type.BINARY; +import java.io.ByteArrayInputStream; import java.io.IOException; +import com.google.common.base.Supplier; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; @@ -31,29 +33,29 @@ import org.apache.jackrabbit.oak.segment.RecordId; import org.apache.jackrabbit.oak.segment.SegmentBlob; import org.apache.jackrabbit.oak.segment.SegmentNodeState; -import org.apache.jackrabbit.oak.segment.standby.store.RemoteSegmentLoader; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; +import org.apache.jackrabbit.oak.segment.file.FileStore; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStateDiff; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -class StandbyApplyDiff implements NodeStateDiff { +class StandbyDiff implements NodeStateDiff { - private static final Logger log = LoggerFactory - .getLogger(StandbyApplyDiff.class); + private static final Logger log = LoggerFactory.getLogger(StandbyDiff.class); private final NodeBuilder builder; - private final StandbyStore store; + private final FileStore store; + private final StandbyClient client; + private final boolean hasDataStore; - private final RemoteSegmentLoader loader; - private final String path; + private final Supplier running; + /** * read-only traversal of the diff that has 2 properties: one is to log all * the content changes, second is to drill down to properly level, so that @@ -61,60 +63,70 @@ */ private final boolean logOnly; - public StandbyApplyDiff(NodeBuilder builder, StandbyStore store, - RemoteSegmentLoader loader) { - this(builder, store, loader, "/", false); + StandbyDiff(NodeBuilder builder, FileStore store, StandbyClient client, Supplier running) { + this(builder, store, client, "/", false, running); } - private StandbyApplyDiff(NodeBuilder builder, StandbyStore store, - RemoteSegmentLoader loader, String path, boolean logOnly) { + private StandbyDiff(NodeBuilder builder, FileStore store, StandbyClient client, String path, boolean logOnly, Supplier running) { this.builder = builder; this.store = store; this.hasDataStore = store.getBlobStore() != null; - this.loader = loader; + this.client = client; this.path = path; this.logOnly = logOnly; + this.running = running; } + private boolean stop() { + return !running.get(); + } + @Override public boolean propertyAdded(PropertyState after) { - if (!loader.isRunning()) { + if (stop()) { return false; } - if (!logOnly) { + + if (logOnly) { + binaryCheck(after); + } else { builder.setProperty(binaryCheck(after)); - } else { - binaryCheck(after); } + return true; } @Override public boolean propertyChanged(PropertyState before, PropertyState after) { - if (!loader.isRunning()) { + if (stop()) { return false; } - if (!logOnly) { + + if (logOnly) { + binaryCheck(after); + } else { builder.setProperty(binaryCheck(after)); - } else { - binaryCheck(after); } + return true; } @Override public boolean propertyDeleted(PropertyState before) { - if (!loader.isRunning()) { + if (stop()) { return false; } + if (!logOnly) { builder.removeProperty(before.getName()); } + return true; } private PropertyState binaryCheck(PropertyState property) { Type type = property.getType(); + if (type == BINARY) { binaryCheck(property.getValue(Type.BINARY), property.getName()); } else if (type == BINARIES) { @@ -122,93 +134,100 @@ binaryCheck(blob, property.getName()); } } + return property; } private void binaryCheck(Blob b, String pName) { if (b instanceof SegmentBlob) { - SegmentBlob sb = (SegmentBlob) b; - // verify if the blob exists - if (sb.isExternal() && hasDataStore && b.getReference() == null) { - String blobId = sb.getBlobId(); - if (blobId != null) { - readBlob(blobId, pName); - } - } + binaryCheck((SegmentBlob) b, pName); } else { - log.warn("Unknown Blob {} at {}, ignoring", b.getClass().getName(), - path + "#" + pName); + log.warn("Unknown Blob {} at {}, ignoring", b.getClass().getName(), path + "#" + pName); } } - private void readBlob(String blobId, String pName) { - Blob read = loader.readBlob(blobId); - if (read != null) { + private void binaryCheck(SegmentBlob sb, String pName) { + if (sb.isExternal() && hasDataStore && sb.getReference() == null) { + String blobId = sb.getBlobId(); + + if (blobId == null) { + return; + } + try { - store.getBlobStore().writeBlob(read.getNewStream()); - } catch (IOException f) { - throw new IllegalStateException("Unable to persist blob " - + blobId + " at " + path + "#" + pName, f); + readBlob(blobId, pName); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } - } else { - throw new IllegalStateException("Unable to load remote blob " - + blobId + " at " + path + "#" + pName); } } + private void readBlob(String blobId, String pName) throws InterruptedException { + byte[] data = client.getBlob(blobId); + + if (data == null) { + throw new IllegalStateException("Unable to load remote blob " + blobId + " at " + path + "#" + pName); + } + + try { + store.getBlobStore().writeBlob(new ByteArrayInputStream(data)); + } catch (IOException f) { + throw new IllegalStateException("Unable to persist blob " + blobId + " at " + path + "#" + pName, f); + } + } + @Override public boolean childNodeAdded(String name, NodeState after) { - return process(name, "childNodeAdded", EmptyNodeState.EMPTY_NODE, - after); + return process(name, "childNodeAdded", EmptyNodeState.EMPTY_NODE, after); } @Override - public boolean childNodeChanged(String name, NodeState before, - NodeState after) { + public boolean childNodeChanged(String name, NodeState before, NodeState after) { return process(name, "childNodeChanged", before, after); } - private boolean process(String name, String op, NodeState before, - NodeState after) { - if (!loader.isRunning()) { + private boolean process(String name, String op, NodeState before, NodeState after) { + if (stop()) { return false; } + if (after instanceof SegmentNodeState) { if (log.isTraceEnabled()) { - log.trace("{} {}, readonly binary check {}", op, path + name, - logOnly); + log.trace("{} {}, readonly binary check {}", op, path + name, logOnly); } + if (!logOnly) { RecordId id = ((SegmentNodeState) after).getRecordId(); - builder.setChildNode(name, store.newSegmentNodeState(id)); + builder.setChildNode(name, store.getReader().readNode(id)); } + if ("checkpoints".equals(name)) { // if we're on the /checkpoints path, there's no need for a deep // traversal to verify binaries return true; } - if (hasDataStore) { - // has external datastore, we need a deep - // traversal to verify binaries - return after.compareAgainstBaseState(before, - new StandbyApplyDiff(builder.getChildNode(name), store, - loader, path + name + "/", true)); - } else { + + if (!hasDataStore) { return true; + } + // has external datastore, we need a deep + // traversal to verify binaries + return after.compareAgainstBaseState(before, new StandbyDiff(builder.getChildNode(name), store, client, path + name + "/", true, running)); } + return false; } @Override public boolean childNodeDeleted(String name, NodeState before) { - if (!loader.isRunning()) { - return false; - } log.trace("childNodeDeleted {}, RO:{}", path + name, logOnly); + if (!logOnly) { builder.getChildNode(name).remove(); } + return true; } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySync.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySync.java (working copy) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySync.java (working copy) @@ -16,12 +16,15 @@ * specific language governing permissions and limitations * under the License. */ + package org.apache.jackrabbit.oak.segment.standby.client; +import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount; + import java.io.Closeable; +import java.io.IOException; import java.lang.management.ManagementFactory; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.management.MBeanServer; @@ -29,61 +32,51 @@ import javax.management.StandardMBean; import javax.net.ssl.SSLException; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.compression.SnappyFramedDecoder; -import io.netty.handler.codec.string.StringEncoder; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.util.InsecureTrustManagerFactory; -import io.netty.handler.timeout.ReadTimeoutHandler; -import io.netty.util.CharsetUtil; -import org.apache.jackrabbit.oak.segment.SegmentStore; +import com.google.common.base.Supplier; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.codec.RecordIdDecoder; import org.apache.jackrabbit.oak.segment.standby.jmx.ClientStandbyStatusMBean; import org.apache.jackrabbit.oak.segment.standby.jmx.StandbyStatusMBean; import org.apache.jackrabbit.oak.segment.standby.store.CommunicationObserver; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public final class StandbyClient implements ClientStandbyStatusMBean, Runnable, Closeable { +public final class StandbySync implements ClientStandbyStatusMBean, Runnable, Closeable { + public static final String CLIENT_ID_PROPERTY_NAME = "standbyID"; - private static final Logger log = LoggerFactory - .getLogger(StandbyClient.class); + private static final Logger log = LoggerFactory.getLogger(StandbySync.class); private final String host; + private final int port; + private final int readTimeoutMs; + private final boolean autoClean; - private final StandbyStore store; private final CommunicationObserver observer; - private StandbyClientHandler handler; - private EventLoopGroup group; - private SslContext sslContext; + + private final boolean secure; + private boolean active = false; + private int failedRequests; + private long lastSuccessfulRequest; + private volatile String state; + private final Object sync = new Object(); + private final FileStore fileStore; + private final AtomicBoolean running = new AtomicBoolean(true); private long syncStartTimestamp; + private long syncEndTimestamp; - public StandbyClient(String host, int port, FileStore store, - boolean secure, int readTimeoutMs, boolean autoClean) - throws SSLException { + public StandbySync(String host, int port, FileStore store, boolean secure, int readTimeoutMs, boolean autoClean) throws SSLException { this.state = STATUS_INITIALIZING; this.lastSuccessfulRequest = -1; this.syncStartTimestamp = -1; @@ -91,20 +84,17 @@ this.failedRequests = 0; this.host = host; this.port = port; - if (secure) { - this.sslContext = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); - } + this.secure = secure; this.readTimeoutMs = readTimeoutMs; this.autoClean = autoClean; - this.store = new StandbyStore(store); + this.fileStore = store; String s = System.getProperty(CLIENT_ID_PROPERTY_NAME); this.observer = new CommunicationObserver((s == null || s.length() == 0) ? UUID.randomUUID().toString() : s); final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); try { jmxServer.registerMBean(new StandardMBean(this, ClientStandbyStatusMBean.class), new ObjectName(this.getMBeanName())); - } - catch (Exception e) { + } catch (Exception e) { log.error("can register standby status mbean", e); } } @@ -119,12 +109,10 @@ final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); try { jmxServer.unregisterMBean(new ObjectName(this.getMBeanName())); - } - catch (Exception e) { + } catch (Exception e) { log.error("can unregister standby status mbean", e); } observer.unregister(); - shutdownNetty(); state = STATUS_CLOSED; } @@ -134,49 +122,32 @@ return; } - Bootstrap b; - synchronized (this.sync) { - if (this.active) { + synchronized (sync) { + if (active) { return; } state = STATUS_STARTING; - handler = new StandbyClientHandler(this.store, observer, running, - readTimeoutMs, autoClean); - group = new NioEventLoopGroup(); - - b = new Bootstrap(); - b.group(group); - b.channel(NioSocketChannel.class); - b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, readTimeoutMs); - b.option(ChannelOption.TCP_NODELAY, true); - b.option(ChannelOption.SO_REUSEADDR, true); - b.option(ChannelOption.SO_KEEPALIVE, true); - - b.handler(new ChannelInitializer() { - @Override - public void initChannel(SocketChannel ch) throws Exception { - ChannelPipeline p = ch.pipeline(); - if (sslContext != null) { - p.addLast(sslContext.newHandler(ch.alloc())); - } - p.addLast("readTimeoutHandler", new ReadTimeoutHandler( - readTimeoutMs, TimeUnit.MILLISECONDS)); - p.addLast(new StringEncoder(CharsetUtil.UTF_8)); - p.addLast(new SnappyFramedDecoder(true)); - p.addLast(new RecordIdDecoder(store)); - p.addLast(handler); - } - }); state = STATUS_RUNNING; - this.active = true; + active = true; } try { long startTimestamp = System.currentTimeMillis(); - // Start the client. - ChannelFuture f = b.connect(host, port).sync(); - // Wait until the connection is closed. - f.channel().closeFuture().sync(); + try (StandbyClient client = new StandbyClient(observer.getID(), secure, readTimeoutMs)) { + client.connect(host, port); + + long sizeBefore = fileStore.getStats().getApproximateSize(); + new StandbySyncExecution(fileStore, client, newRunningSupplier()).execute(); + long sizeAfter = fileStore.getStats().getApproximateSize(); + + if (autoClean && sizeBefore > 0) { + // if size gain is over 25% call cleanup + if (sizeAfter - sizeBefore > 0.25 * sizeBefore) { + log.info("Store size increased from {} to {}, will run cleanup.", humanReadableByteCount(sizeBefore), humanReadableByteCount(sizeAfter)); + fileStore.cleanup(); + } + } + } this.failedRequests = 0; this.syncStartTimestamp = startTimestamp; this.syncEndTimestamp = System.currentTimeMillis(); @@ -187,20 +158,19 @@ } finally { synchronized (this.sync) { this.active = false; - shutdownNetty(); } } } - private void shutdownNetty() { - if (handler != null) { - handler.close(); - handler = null; - } - if (group != null && !group.isShuttingDown()) { - group.shutdownGracefully(0, 1, TimeUnit.SECONDS).syncUninterruptibly(); - group = null; - } + private Supplier newRunningSupplier() { + return new Supplier() { + + @Override + public Boolean get() { + return running.get(); + } + + }; } @Override @@ -237,8 +207,10 @@ @Override public int getSecondsSinceLastSuccess() { - if (this.lastSuccessfulRequest < 0) return -1; - return (int)(System.currentTimeMillis() / 1000 - this.lastSuccessfulRequest); + if (this.lastSuccessfulRequest < 0) { + return -1; + } + return (int) (System.currentTimeMillis() / 1000 - this.lastSuccessfulRequest); } @Override @@ -253,7 +225,11 @@ @Override public void cleanup() { - store.cleanup(); + try { + fileStore.cleanup(); + } catch (IOException e) { + log.error("Error while cleaning up", e); + } } @Override Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySyncExecution.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySyncExecution.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySyncExecution.java (working copy) @@ -0,0 +1,167 @@ +/* + * 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.segment.standby.client; + +import static com.google.common.collect.Maps.newHashMap; +import static com.google.common.collect.Sets.newHashSet; + +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nonnull; + +import com.google.common.base.Supplier; +import org.apache.jackrabbit.oak.segment.RecordId; +import org.apache.jackrabbit.oak.segment.Segment; +import org.apache.jackrabbit.oak.segment.SegmentId; +import org.apache.jackrabbit.oak.segment.SegmentNodeBuilder; +import org.apache.jackrabbit.oak.segment.SegmentNodeState; +import org.apache.jackrabbit.oak.segment.SegmentNotFoundException; +import org.apache.jackrabbit.oak.segment.file.FileStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Encapsulates the algorithm for a single execution of the synchronization + * process between the primary and the standby instance. It also contains + * temporary state that is supposed to be used for the lifetime of a + * synchronization run. + */ +class StandbySyncExecution { + + private static final Logger log = LoggerFactory.getLogger(StandbySyncExecution.class); + + private final FileStore store; + + private final StandbyClient client; + + private final Supplier running; + + private final Set visited = newHashSet(); + + private final Map cache = newHashMap(); + + StandbySyncExecution(FileStore store, StandbyClient client, Supplier running) { + this.store = store; + this.client = client; + this.running = running; + } + + void execute() throws Exception { + RecordId remoteHead = getHead(); + + if (remoteHead.equals(store.getHead().getRecordId())) { + return; + } + + long t = System.currentTimeMillis(); + SegmentNodeState before = store.getHead(); + SegmentNodeBuilder builder = before.builder(); + SegmentNodeState current = newSegmentNodeState(remoteHead); + compareAgainstBaseState(current, before, builder); + boolean ok = setHead(before, builder.getNodeState()); + log.debug("updated head state successfully: {} in {}ms.", ok, System.currentTimeMillis() - t); + } + + private RecordId getHead() throws Exception { + return RecordId.fromString(store, client.getHead()); + } + + private SegmentNodeState newSegmentNodeState(RecordId id) { + return store.getReader().readNode(id); + } + + private boolean setHead(@Nonnull SegmentNodeState expected, @Nonnull SegmentNodeState head) { + return store.getRevisions().setHead(expected.getRecordId(), head.getRecordId()); + } + + private boolean compareAgainstBaseState(SegmentNodeState current, SegmentNodeState before, SegmentNodeBuilder builder) throws Exception { + while (true) { + try { + return current.compareAgainstBaseState(before, new StandbyDiff(builder, store, client, running)); + } catch (SegmentNotFoundException e) { + log.info("Found missing segment {}", e.getSegmentId()); + copySegmentFromPrimary(UUID.fromString(e.getSegmentId())); + } + } + } + + private void copySegmentFromPrimary(UUID segmentId) throws Exception { + LinkedList batch = new LinkedList<>(); + + batch.offer(segmentId); + + while (batch.size() > 0) { + UUID current = batch.remove(); + + log.info("Loading segment {}", current); + Segment segment = copySingleSegmentFromPrimary(current); + + log.info("Marking segment {} as loaded", current); + visited.add(current); + + if (!SegmentId.isDataSegmentId(current.getLeastSignificantBits())) { + continue; + } + + log.info("Inspecting segment {} for references", current); + + for (int i = 0; i < segment.getReferencedSegmentIdCount(); i++) { + UUID referenced = segment.getReferencedSegmentId(i); + + if (visited.contains(referenced)) { + continue; + } + + log.info("Found reference from {} to {}", current, referenced); + + if (SegmentId.isDataSegmentId(referenced.getLeastSignificantBits())) { + batch.add(referenced); + } else { + batch.addFirst(referenced); + } + } + } + } + + private Segment copySingleSegmentFromPrimary(UUID uuid) throws Exception { + Segment result = cache.get(uuid); + + if (result != null) { + log.info("Segment {} was found in the local cache", uuid); + return result; + } + + byte[] data = client.getSegment(uuid.toString()); + + if (data == null) { + throw new IllegalStateException("Unable to read segment " + uuid); + } + + long msb = uuid.getMostSignificantBits(); + long lsb = uuid.getLeastSignificantBits(); + SegmentId segmentId = store.newSegmentId(msb, lsb); + store.writeSegment(segmentId, data, 0, data.length); + result = segmentId.getSegment(); + cache.put(uuid, result); + return result; + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/client/StandbySyncExecution.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/BlobEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/BlobEncoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/BlobEncoder.java (working copy) @@ -1,65 +0,0 @@ -/* - * 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.segment.standby.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import java.io.InputStream; -import java.nio.charset.Charset; - -import org.apache.commons.io.IOUtils; -import org.apache.jackrabbit.oak.api.Blob; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; - -public class BlobEncoder extends MessageToByteEncoder { - - // TODO - // if transferring large binaries turns out to be too intensive look into - // using a ChunkedWriteHandler and a new ChunkedStream(Blob.getNewStream()) - - @Override - protected void encode(ChannelHandlerContext ctx, Blob b, ByteBuf out) - throws Exception { - byte[] bytes = null; - InputStream s = b.getNewStream(); - try { - bytes = IOUtils.toByteArray(s); - } finally { - s.close(); - } - - Hasher hasher = Hashing.murmur3_32().newHasher(); - long hash = hasher.putBytes(bytes).hash().padToLong(); - - out.writeInt(bytes.length); - out.writeByte(Messages.HEADER_BLOB); - - String bid = b.getContentIdentity(); - byte[] id = bid.getBytes(Charset.forName("UTF-8")); - out.writeInt(id.length); - out.writeBytes(id); - - out.writeLong(hash); - out.writeBytes(bytes); - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestDecoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestDecoder.java (working copy) @@ -1,43 +0,0 @@ -/* - * 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.segment.standby.codec; - -import java.util.List; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GetBlobRequestDecoder extends MessageToMessageDecoder { - - private static final Logger log = LoggerFactory.getLogger(GetBlobRequestDecoder.class); - - @Override - protected void decode(ChannelHandlerContext ctx, String msg, List out) throws Exception { - String request = Messages.extractMessageFrom(msg); - - if (request != null && request.startsWith(Messages.GET_BLOB)) { - log.debug("Parsed 'get blob' message"); - out.add(new GetBlobRequest(Messages.extractClientFrom(msg), request.substring(Messages.GET_BLOB.length()))); - } else { - ctx.fireChannelRead(msg); - } - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestEncoder.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestEncoder.java (working copy) @@ -0,0 +1,37 @@ +/* + * 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.segment.standby.codec; + +import java.util.List; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GetBlobRequestEncoder extends MessageToMessageEncoder { + + private final Logger log = LoggerFactory.getLogger(GetBlobRequestEncoder.class); + + @Override + protected void encode(ChannelHandlerContext ctx, GetBlobRequest msg, List out) throws Exception { + log.debug("Sending request from client {} for blob {}", msg.getClientId(), msg.getBlobId()); + out.add(Messages.newGetBlobRequest(msg.getClientId(), msg.getBlobId())); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestEncoder.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponse.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponse.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponse.java (working copy) @@ -17,25 +17,30 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import org.apache.jackrabbit.oak.api.Blob; - public class GetBlobResponse { private final String clientId; - private final Blob blob; + private final String blobId; - public GetBlobResponse(String clientId, Blob blob) { + private final byte[] blobData; + + public GetBlobResponse(String clientId, String blobId, byte[] blobData) { this.clientId = clientId; - this.blob = blob; + this.blobId = blobId; + this.blobData = blobData; } public String getClientId() { return clientId; } - public Blob getBlob() { - return blob; + public String getBlobId() { + return blobId; } + public byte[] getBlobData() { + return blobData; + } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponseEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponseEncoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponseEncoder.java (working copy) @@ -17,7 +17,6 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import java.io.InputStream; import java.nio.charset.Charset; import com.google.common.hash.Hasher; @@ -25,8 +24,6 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import org.apache.commons.io.IOUtils; -import org.apache.jackrabbit.oak.api.Blob; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,30 +33,22 @@ @Override protected void encode(ChannelHandlerContext ctx, GetBlobResponse msg, ByteBuf out) throws Exception { - log.debug("Sending blob {} to client {}", msg.getBlob().getContentIdentity(), msg.getClientId()); - encode(msg.getBlob(), out); + log.debug("Sending blob {} to client {}", msg.getBlobId(), msg.getClientId()); + encode(msg.getBlobId(), msg.getBlobData(), out); } - private void encode(Blob b, ByteBuf out) throws Exception { - byte[] bytes; + private void encode(String blobId, byte[] data, ByteBuf out) throws Exception { + byte[] blobIdBytes = blobId.getBytes(Charset.forName("UTF-8")); - try (InputStream s = b.getNewStream()) { - bytes = IOUtils.toByteArray(s); - } - Hasher hasher = Hashing.murmur3_32().newHasher(); - long hash = hasher.putBytes(bytes).hash().padToLong(); + long hash = hasher.putBytes(data).hash().padToLong(); - out.writeInt(bytes.length); + out.writeInt(1 + 4 + blobIdBytes.length + 8 + data.length); out.writeByte(Messages.HEADER_BLOB); - - String bid = b.getContentIdentity(); - byte[] id = bid.getBytes(Charset.forName("UTF-8")); - out.writeInt(id.length); - out.writeBytes(id); - + out.writeInt(blobIdBytes.length); + out.writeBytes(blobIdBytes); out.writeLong(hash); - out.writeBytes(bytes); + out.writeBytes(data); } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestDecoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestDecoder.java (working copy) @@ -1,42 +0,0 @@ -/* - * 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.segment.standby.codec; - -import java.util.List; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GetHeadRequestDecoder extends MessageToMessageDecoder { - - private static final Logger log = LoggerFactory.getLogger(GetHeadRequestDecoder.class); - - @Override - protected void decode(ChannelHandlerContext ctx, String msg, List out) throws Exception { - String request = Messages.extractMessageFrom(msg); - - if (request != null && request.equalsIgnoreCase(Messages.GET_HEAD)) { - log.debug("Parsed 'get head' message"); - out.add(new GetHeadRequest(Messages.extractClientFrom(msg))); - } else { - ctx.fireChannelRead(msg); - } - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestEncoder.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestEncoder.java (working copy) @@ -0,0 +1,37 @@ +/* + * 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.segment.standby.codec; + +import java.util.List; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GetHeadRequestEncoder extends MessageToMessageEncoder { + + private static final Logger log = LoggerFactory.getLogger(GetHeadRequestEncoder.class); + + @Override + protected void encode(ChannelHandlerContext ctx, GetHeadRequest msg, List out) throws Exception { + log.debug("Sending request from client {} for current head", msg.getClientId()); + out.add(Messages.newGetHeadRequest(msg.getClientId())); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestEncoder.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponse.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponse.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponse.java (working copy) @@ -17,15 +17,13 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import org.apache.jackrabbit.oak.segment.RecordId; - public class GetHeadResponse { private final String clientId; - private final RecordId headRecordId; + private final String headRecordId; - public GetHeadResponse(String clientId, RecordId headRecordId) { + public GetHeadResponse(String clientId, String headRecordId) { this.clientId = clientId; this.headRecordId = headRecordId; } @@ -34,7 +32,7 @@ return clientId; } - public RecordId getHeadRecordId() { + public String getHeadRecordId() { return headRecordId; } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponseEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponseEncoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponseEncoder.java (working copy) @@ -34,7 +34,7 @@ @Override protected void encode(ChannelHandlerContext ctx, GetHeadResponse msg, ByteBuf out) throws Exception { log.debug("Sending head {} to client {}", msg.getHeadRecordId(), msg.getClientId()); - byte[] body = msg.getHeadRecordId().toString().getBytes(CharsetUtil.UTF_8); + byte[] body = msg.getHeadRecordId().getBytes(CharsetUtil.UTF_8); out.writeInt(body.length + 1); out.writeByte(Messages.HEADER_RECORD); out.writeBytes(body); Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequest.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequest.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequest.java (working copy) @@ -17,15 +17,13 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import java.util.UUID; - public class GetSegmentRequest { private final String clientId; - private final UUID segmentId; + private final String segmentId; - public GetSegmentRequest(String clientId, UUID segmentId) { + public GetSegmentRequest(String clientId, String segmentId) { this.clientId = clientId; this.segmentId = segmentId; } @@ -34,7 +32,7 @@ return clientId; } - public UUID getSegmentId() { + public String getSegmentId() { return segmentId; } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestDecoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestDecoder.java (working copy) @@ -1,44 +0,0 @@ -/* - * 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.segment.standby.codec; - -import java.util.List; -import java.util.UUID; - -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToMessageDecoder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GetSegmentRequestDecoder extends MessageToMessageDecoder { - - private static final Logger log = LoggerFactory.getLogger(GetSegmentRequestDecoder.class); - - @Override - protected void decode(ChannelHandlerContext ctx, String msg, List out) throws Exception { - String request = Messages.extractMessageFrom(msg); - - if (request != null && request.startsWith(Messages.GET_SEGMENT)) { - log.debug("Parsed 'get segment' message"); - out.add(new GetSegmentRequest(Messages.extractClientFrom(msg), UUID.fromString(request.substring(Messages.GET_SEGMENT.length())))); - } else { - ctx.fireChannelRead(msg); - } - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestEncoder.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestEncoder.java (working copy) @@ -0,0 +1,37 @@ +/* + * 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.segment.standby.codec; + +import java.util.List; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GetSegmentRequestEncoder extends MessageToMessageEncoder { + + private final Logger log = LoggerFactory.getLogger(GetSegmentRequestEncoder.class); + + @Override + protected void encode(ChannelHandlerContext ctx, GetSegmentRequest msg, List out) throws Exception { + log.debug("Sending request from client {} for segment {}", msg.getClientId(), msg.getSegmentId()); + out.add(Messages.newGetSegmentRequest(msg.getClientId(), msg.getSegmentId())); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestEncoder.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponse.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponse.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponse.java (working copy) @@ -17,25 +17,30 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import org.apache.jackrabbit.oak.segment.Segment; - public class GetSegmentResponse { private final String clientId; - private final Segment segment; + private final String segmentId; - public GetSegmentResponse(String clientId, Segment segment) { + private final byte[] segmentData; + + public GetSegmentResponse(String clientId, String segmentId, byte[] segmentData) { this.clientId = clientId; - this.segment = segment; + this.segmentId = segmentId; + this.segmentData = segmentData; } public String getClientId() { return clientId; } - public Segment getSegment() { - return segment; + public String getSegmentId() { + return segmentId; } + public byte[] getSegmentData() { + return segmentData; + } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponseEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponseEncoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponseEncoder.java (working copy) @@ -17,15 +17,13 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import java.io.ByteArrayOutputStream; +import java.util.UUID; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import org.apache.jackrabbit.oak.segment.Segment; -import org.apache.jackrabbit.oak.segment.SegmentId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,27 +40,23 @@ @Override protected void encode(ChannelHandlerContext ctx, GetSegmentResponse msg, ByteBuf out) throws Exception { - log.debug("Sending segment {} to client {}", msg.getSegment().getSegmentId(), msg.getClientId()); - encode(msg.getSegment(), out); + log.debug("Sending segment {} to client {}", msg.getSegmentId(), msg.getClientId()); + encode(msg.getSegmentId(), msg.getSegmentData(), out); } - private void encode(Segment s, ByteBuf out) throws Exception { - SegmentId id = s.getSegmentId(); + private void encode(String segmentId, byte[] data, ByteBuf out) throws Exception { + UUID id = UUID.fromString(segmentId); - ByteArrayOutputStream baos = new ByteArrayOutputStream(s.size()); - s.writeTo(baos); - byte[] segment = baos.toByteArray(); - Hasher hasher = Hashing.murmur3_32().newHasher(); - long hash = hasher.putBytes(segment).hash().padToLong(); + long hash = hasher.putBytes(data).hash().padToLong(); - int len = segment.length + EXTRA_HEADERS_WO_SIZE; + int len = data.length + EXTRA_HEADERS_WO_SIZE; out.writeInt(len); out.writeByte(Messages.HEADER_SEGMENT); out.writeLong(id.getMostSignificantBits()); out.writeLong(id.getLeastSignificantBits()); out.writeLong(hash); - out.writeBytes(segment); + out.writeBytes(data); } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/IdArrayBasedBlob.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/IdArrayBasedBlob.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/IdArrayBasedBlob.java (working copy) @@ -1,36 +0,0 @@ -/* - * 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.segment.standby.codec; - -import org.apache.jackrabbit.oak.plugins.memory.ArrayBasedBlob; - -public class IdArrayBasedBlob extends ArrayBasedBlob { - - private final String blobId; - - public IdArrayBasedBlob(byte[] value, String blobId) { - super(value); - this.blobId = blobId; - } - - public String getBlobId() { - return blobId; - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/Messages.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/Messages.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/Messages.java (working copy) @@ -16,19 +16,25 @@ * specific language governing permissions and limitations * under the License. */ + package org.apache.jackrabbit.oak.segment.standby.codec; -public class Messages { +class Messages { - public static final byte HEADER_RECORD = 0x00; - public static final byte HEADER_SEGMENT = 0x01; - public static final byte HEADER_BLOB = 0x02; + static final byte HEADER_RECORD = 0x00; - public static final String GET_HEAD = "h"; - public static final String GET_SEGMENT = "s."; - public static final String GET_BLOB = "b."; + static final byte HEADER_SEGMENT = 0x01; + static final byte HEADER_BLOB = 0x02; + + static final String GET_HEAD = "h"; + + static final String GET_SEGMENT = "s."; + + static final String GET_BLOB = "b."; + private static final String MAGIC = "Standby-CMD@"; + private static final String SEPARATOR = ":"; private static String newRequest(String clientId, String body, boolean delimited) { @@ -48,31 +54,31 @@ return builder.toString(); } - public static String newGetHeadRequest(String clientId, boolean delimited) { + static String newGetHeadRequest(String clientId, boolean delimited) { return newRequest(clientId, GET_HEAD, delimited); } - public static String newGetHeadRequest(String clientId) { + static String newGetHeadRequest(String clientId) { return newGetHeadRequest(clientId, true); } - public static String newGetSegmentRequest(String clientId, String segmentId, boolean delimited) { + static String newGetSegmentRequest(String clientId, String segmentId, boolean delimited) { return newRequest(clientId, GET_SEGMENT + segmentId, delimited); } - public static String newGetSegmentRequest(String clientId, String segmentId) { + static String newGetSegmentRequest(String clientId, String segmentId) { return newGetSegmentRequest(clientId, segmentId, true); } - public static String newGetBlobRequest(String clientId, String blobId, boolean delimited) { + static String newGetBlobRequest(String clientId, String blobId, boolean delimited) { return newRequest(clientId, GET_BLOB + blobId, delimited); } - public static String newGetBlobRequest(String clientId, String blobId) { + static String newGetBlobRequest(String clientId, String blobId) { return newGetBlobRequest(clientId, blobId, true); } - public static String extractMessageFrom(String payload) { + static String extractMessageFrom(String payload) { if (payload.startsWith(MAGIC) && payload.length() > MAGIC.length()) { int i = payload.indexOf(SEPARATOR); return payload.substring(i + 1); @@ -80,7 +86,7 @@ return null; } - public static String extractClientFrom(String payload) { + static String extractClientFrom(String payload) { if (payload.startsWith(MAGIC) && payload.length() > MAGIC.length()) { payload = payload.substring(MAGIC.length()); int i = payload.indexOf(SEPARATOR); @@ -88,4 +94,5 @@ } return null; } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RecordIdDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RecordIdDecoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RecordIdDecoder.java (working copy) @@ -1,70 +0,0 @@ -/* - * 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.segment.standby.codec; - -import java.io.IOException; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.util.CharsetUtil; -import org.apache.jackrabbit.oak.segment.RecordId; -import org.apache.jackrabbit.oak.segment.SegmentStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class RecordIdDecoder extends LengthFieldBasedFrameDecoder { - - private static final Logger log = LoggerFactory - .getLogger(RecordIdDecoder.class); - - private final SegmentStore store; - - public RecordIdDecoder(SegmentStore store) { - super(64, 0, 4, 0, 4); - this.store = store; - } - - @Override - protected Object decode(ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - ByteBuf frame = (ByteBuf) super.decode(ctx, in); - if (frame == null) { - throw new IOException("Received unexpected empty frame. Maybe you have enabled secure transmission on only one endpoint of the connection."); - } - byte type = frame.readByte(); - frame.discardReadBytes(); - String id = frame.toString(CharsetUtil.UTF_8); - try { - log.debug("received type {} with id {}", type, id); - return RecordId.fromString(store, id); - } catch (IllegalArgumentException e) { - log.error(e.getMessage(), e); - } - return null; - } - - @Override - protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, - int index, int length) { - return buffer.slice(index, length); - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RecordIdEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RecordIdEncoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RecordIdEncoder.java (working copy) @@ -1,39 +0,0 @@ -/* - * 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.segment.standby.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; -import io.netty.util.CharsetUtil; - -import org.apache.jackrabbit.oak.segment.RecordId; - -public class RecordIdEncoder extends MessageToByteEncoder { - - @Override - protected void encode(ChannelHandlerContext ctx, RecordId msg, ByteBuf out) - throws Exception { - byte[] body = msg.toString().getBytes(CharsetUtil.UTF_8); - out.writeInt(body.length + 1); - out.writeByte(Messages.HEADER_RECORD); - out.writeBytes(body); - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ReplyDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ReplyDecoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ReplyDecoder.java (working copy) @@ -1,185 +0,0 @@ -/* - * 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.segment.standby.codec; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.List; -import java.util.UUID; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ReplayingDecoder; -import org.apache.jackrabbit.oak.segment.Segment; -import org.apache.jackrabbit.oak.segment.SegmentId; -import org.apache.jackrabbit.oak.segment.standby.codec.ReplyDecoder.DecodingState; -import org.apache.jackrabbit.oak.segment.standby.store.StandbyStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ReplyDecoder extends ReplayingDecoder { - - private static final int REPLY_HEADER_SIZE = 25; - - public enum DecodingState { - HEADER, SEGMENT, BLOB - } - - private static final Logger log = LoggerFactory.getLogger(ReplyDecoder.class); - - private final StandbyStore store; - - private int length = -1; - - private byte type = -1; - - public ReplyDecoder(StandbyStore store) { - super(DecodingState.HEADER); - this.store = store; - } - - private void reset() { - checkpoint(DecodingState.HEADER); - length = -1; - type = -1; - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { - switch (state()) { - case HEADER: { - log.debug("Parsing header"); - length = in.readInt(); - type = in.readByte(); - switch (type) { - case Messages.HEADER_SEGMENT: - checkpoint(DecodingState.SEGMENT); - break; - case Messages.HEADER_BLOB: - checkpoint(DecodingState.BLOB); - break; - default: - throw new Exception("Unknown type: " + type); - } - return; - } - - case SEGMENT: { - log.debug("Parsing segment"); - Segment s = decodeSegment(in, length, type); - if (s != null) { - out.add(new SegmentReply(s)); - reset(); - } - return; - } - - case BLOB: { - log.debug("Parsing blob"); - IdArrayBasedBlob b = decodeBlob(in, length, type); - if (b != null) { - out.add(new SegmentReply(b)); - reset(); - } - return; - } - - default: - log.error("Message state unknown"); - throw new Exception("Unknown decoding state: " + state()); - } - } - - private Segment decodeSegment(ByteBuf in, int len, byte type) { - log.debug("Decoding segment, length={}, type={}", len - REPLY_HEADER_SIZE, type); - - long msb = in.readLong(); - long lsb = in.readLong(); - long hash = in.readLong(); - - if (log.isDebugEnabled()) { - log.debug("Decoding segment, id={}", new UUID(msb, lsb)); - } - - // #readBytes throws a 'REPLAY' exception if there are not enough bytes - // available for reading - byte[] segment = readSegmentBytes(in.readBytes(len - REPLY_HEADER_SIZE)); - - if (log.isDebugEnabled()) { - log.debug("Verifying segment, id={}", new UUID(msb, lsb)); - } - - Hasher hasher = Hashing.murmur3_32().newHasher(); - long check = hasher.putBytes(segment).hash().padToLong(); - - if (hash == check) { - SegmentId id = store.newSegmentId(msb, lsb); - log.debug("Segment verified, id={}", id); - return store.newSegment(id, ByteBuffer.wrap(segment)); - } - - if (log.isDebugEnabled()) { - log.debug("Segment corrupted, id={}", new UUID(msb, lsb)); - } - - return null; - } - - private byte[] readSegmentBytes(ByteBuf data) { - if (data.hasArray()) { - return data.array(); - } - - byte[] result = new byte[data.readableBytes()]; - data.readBytes(result); - return result; - } - - private IdArrayBasedBlob decodeBlob(ByteBuf in, int length, byte type) { - int inIdLen = in.readInt(); - byte[] bid = new byte[inIdLen]; - in.readBytes(bid); - String id = new String(bid, Charset.forName("UTF-8")); - - long hash = in.readLong(); - // #readBytes throws a 'REPLAY' exception if there are not enough bytes - // available for reading - ByteBuf data = in.readBytes(length); - byte[] blob; - if (data.hasArray()) { - blob = data.array(); - } else { - blob = new byte[length]; - data.readBytes(blob); - } - - Hasher hasher = Hashing.murmur3_32().newHasher(); - long check = hasher.putBytes(blob).hash().padToLong(); - if (hash == check) { - log.debug("received blob with id {} and size {}", id, blob.length); - return new IdArrayBasedBlob(blob, id); - } - log.debug("received corrupted binary {}, ignoring", id); - return null; - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoder.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoder.java (working copy) @@ -0,0 +1,52 @@ +/* + * 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.segment.standby.codec; + +import java.util.List; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RequestDecoder extends MessageToMessageDecoder { + + private static final Logger log = LoggerFactory.getLogger(RequestDecoder.class); + + @Override + protected void decode(ChannelHandlerContext ctx, String msg, List out) throws Exception { + String request = Messages.extractMessageFrom(msg); + + if (request == null) { + log.debug("Received invalid message {}, ignoring", msg); + ctx.fireChannelRead(msg); + } else if (request.startsWith(Messages.GET_BLOB)) { + log.debug("Parsed 'get blob' request"); + out.add(new GetBlobRequest(Messages.extractClientFrom(msg), request.substring(Messages.GET_BLOB.length()))); + } else if (request.equalsIgnoreCase(Messages.GET_HEAD)) { + log.debug("Parsed 'get head' message"); + out.add(new GetHeadRequest(Messages.extractClientFrom(msg))); + } else if (request.startsWith(Messages.GET_SEGMENT)) { + log.debug("Parsed 'get segment' message"); + out.add(new GetSegmentRequest(Messages.extractClientFrom(msg), request.substring(Messages.GET_SEGMENT.length()))); + } else { + log.debug("Received unrecognizable message {}, dropping", msg); + } + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoder.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoder.java (revision 0) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoder.java (working copy) @@ -0,0 +1,108 @@ +/* + * 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.segment.standby.codec; + +import java.util.List; +import java.util.UUID; + +import com.google.common.base.Charsets; +import com.google.common.hash.Hashing; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResponseDecoder extends ByteToMessageDecoder { + + private static final Logger log = LoggerFactory.getLogger(ResponseDecoder.class); + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { + int length = in.readInt(); + + switch (in.readByte()) { + case Messages.HEADER_RECORD: + log.debug("Decoding 'get head' response"); + decodeGetHeadResponse(length, in, out); + break; + case Messages.HEADER_SEGMENT: + log.debug("Decoding 'get segment' response"); + decodeGetSegmentResponse(length, in, out); + break; + case Messages.HEADER_BLOB: + log.debug("Decoding 'get blob' response"); + decodeGetBlobResponse(length, in, out); + break; + default: + log.debug("Invalid type, dropping message"); + } + } + + private void decodeGetHeadResponse(int length, ByteBuf in, List out) { + byte[] data = new byte[length - 1]; + in.readBytes(data); + String recordId = new String(data, Charsets.UTF_8); + out.add(new GetHeadResponse(null, recordId)); + } + + private void decodeGetSegmentResponse(int length, ByteBuf in, List out) { + long msb = in.readLong(); + long lsb = in.readLong(); + + String segmentId = new UUID(msb, lsb).toString(); + + long hash = in.readLong(); + + byte[] data = new byte[length - 25]; + in.readBytes(data); + + if (hash(data) != hash) { + log.debug("Invalid checksum, discarding segment {}", segmentId); + return; + } + + out.add(new GetSegmentResponse(null, segmentId, data)); + } + + private void decodeGetBlobResponse(int length, ByteBuf in, List out) { + int blobIdLength = in.readInt(); + + byte[] blobIdBytes = new byte[blobIdLength]; + in.readBytes(blobIdBytes); + + String blobId = new String(blobIdBytes, Charsets.UTF_8); + + long hash = in.readLong(); + + byte[] blobData = new byte[length - 1 - 4 - blobIdBytes.length - 8]; + in.readBytes(blobData); + + if (hash(blobData) != hash) { + log.debug("Invalid checksum, discarding blob {}", blobId); + return; + } + + out.add(new GetBlobResponse(null, blobId, blobData)); + } + + private long hash(byte[] data) { + return Hashing.murmur3_32().newHasher().putBytes(data).hash().padToLong(); + } + +} Property changes on: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoder.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/SegmentEncoder.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/SegmentEncoder.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/SegmentEncoder.java (working copy) @@ -1,73 +0,0 @@ -/* - * 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.segment.standby.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -import java.io.ByteArrayOutputStream; - -import org.apache.jackrabbit.oak.segment.Segment; -import org.apache.jackrabbit.oak.segment.SegmentId; - -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; - -public class SegmentEncoder extends MessageToByteEncoder { - - /** - * A segment message is composed of: - * - *
-     *  - (4 bytes) the message length
-     *  - (1 byte ) a message type (not currently used)
-     *  - (8 bytes) segment id most significant bits
-     *  - (8 bytes) segment id least significant bits
-     *  - (8 bytes) checksum hash
-     * 
- */ - static int EXTRA_HEADERS_LEN = 29; - - /** - * the header size not including the length - */ - private int EXTRA_HEADERS_WO_SIZE = EXTRA_HEADERS_LEN - 4; - - @Override - protected void encode(ChannelHandlerContext ctx, Segment s, ByteBuf out) - throws Exception { - SegmentId id = s.getSegmentId(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(s.size()); - s.writeTo(baos); - byte[] segment = baos.toByteArray(); - - Hasher hasher = Hashing.murmur3_32().newHasher(); - long hash = hasher.putBytes(segment).hash().padToLong(); - - int len = segment.length + EXTRA_HEADERS_WO_SIZE; - out.writeInt(len); - out.writeByte(Messages.HEADER_SEGMENT); - out.writeLong(id.getMostSignificantBits()); - out.writeLong(id.getLeastSignificantBits()); - out.writeLong(hash); - out.writeBytes(segment); - } -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/SegmentReply.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/SegmentReply.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/codec/SegmentReply.java (working copy) @@ -1,66 +0,0 @@ -/* - * 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.segment.standby.codec; - -import org.apache.jackrabbit.oak.segment.Segment; - -public class SegmentReply { - - public static final int SEGMENT = 0; - public static final int BLOB = 1; - - public static SegmentReply empty() { - return new SegmentReply(); - } - - private final int type; - - private final Segment segment; - - private final IdArrayBasedBlob blob; - - public SegmentReply(Segment segment) { - this.type = SEGMENT; - this.segment = segment; - this.blob = null; - } - - public SegmentReply(IdArrayBasedBlob blob) { - this.type = BLOB; - this.segment = null; - this.blob = blob; - } - - private SegmentReply() { - this.type = -1; - this.segment = null; - this.blob = null; - } - - public Segment getSegment() { - return this.segment; - } - - public IdArrayBasedBlob getBlob() { - return blob; - } - - public int getType() { - return type; - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbyBlobReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbyBlobReader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbyBlobReader.java (working copy) @@ -17,13 +17,20 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import org.apache.jackrabbit.oak.api.Blob; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.io.IOUtils; import org.apache.jackrabbit.oak.plugins.blob.BlobStoreBlob; import org.apache.jackrabbit.oak.segment.file.FileStore; import org.apache.jackrabbit.oak.spi.blob.BlobStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class DefaultStandbyBlobReader implements StandbyBlobReader { + private final Logger log = LoggerFactory.getLogger(DefaultStandbyBlobReader.class); + private final FileStore store; DefaultStandbyBlobReader(FileStore store) { @@ -31,14 +38,22 @@ } @Override - public Blob readBlob(String blobId) { + public byte[] readBlob(String blobId) { BlobStore blobStore = store.getBlobStore(); - if (blobStore != null) { - return new BlobStoreBlob(blobStore, blobId); + if (blobStore == null) { + return null; } - return null; + byte[] bytes = null; + + try (InputStream s = new BlobStoreBlob(blobStore, blobId).getNewStream()) { + bytes = IOUtils.toByteArray(s); + } catch (IOException e) { + log.warn("Error while reading blob content", e); + } + + return bytes; } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbyHeadReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbyHeadReader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbyHeadReader.java (working copy) @@ -17,7 +17,6 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import org.apache.jackrabbit.oak.segment.RecordId; import org.apache.jackrabbit.oak.segment.file.FileStore; class DefaultStandbyHeadReader implements StandbyHeadReader { @@ -29,8 +28,8 @@ } @Override - public RecordId readHeadRecordId() { - return store.getHead().getRecordId(); + public String readHeadRecordId() { + return store.getHead().getRecordId().toString(); } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbySegmentReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbySegmentReader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/DefaultStandbySegmentReader.java (working copy) @@ -17,9 +17,11 @@ package org.apache.jackrabbit.oak.segment.standby.server; +import java.io.IOException; import java.util.UUID; import java.util.concurrent.TimeUnit; +import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.jackrabbit.oak.segment.Segment; import org.apache.jackrabbit.oak.segment.SegmentId; import org.apache.jackrabbit.oak.segment.SegmentNotFoundException; @@ -38,12 +40,27 @@ } @Override - public Segment readSegment(UUID uuid) { + public byte[] readSegment(String segmentId) { + UUID uuid = UUID.fromString(segmentId); long msb = uuid.getMostSignificantBits(); long lsb = uuid.getLeastSignificantBits(); - SegmentId id = store.newSegmentId(msb, lsb); + Segment segment = readSegment(store.newSegmentId(msb, lsb)); + if (segment == null) { + return null; + } + + try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) { + segment.writeTo(stream); + return stream.toByteArray(); + } catch (IOException e) { + log.warn("Error while reading segment content", e); + return null; + } + } + + private Segment readSegment(SegmentId id) { for (int i = 0; i < 10; i++) { try { return store.readSegment(id); Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetBlobRequestHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetBlobRequestHandler.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetBlobRequestHandler.java (working copy) @@ -19,7 +19,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobRequest; import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobResponse; import org.slf4j.Logger; @@ -39,14 +38,14 @@ protected void channelRead0(ChannelHandlerContext ctx, GetBlobRequest msg) throws Exception { log.debug("Reading blob {} for client {}", msg.getBlobId(), msg.getClientId()); - Blob blob = reader.readBlob(msg.getBlobId()); + byte[] data = reader.readBlob(msg.getBlobId()); - if (blob == null) { + if (data == null) { log.debug("Blob {} not found, discarding request from client {}", msg.getBlobId(), msg.getClientId()); return; } - ctx.writeAndFlush(new GetBlobResponse(msg.getClientId(), blob)); + ctx.writeAndFlush(new GetBlobResponse(msg.getClientId(), msg.getBlobId(), data)); } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetHeadRequestHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetHeadRequestHandler.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetHeadRequestHandler.java (working copy) @@ -19,7 +19,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; -import org.apache.jackrabbit.oak.segment.RecordId; import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadRequest; import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponse; import org.slf4j.Logger; @@ -43,7 +42,7 @@ protected void channelRead0(ChannelHandlerContext ctx, GetHeadRequest msg) throws Exception { log.debug("Reading head for client {}", msg.getClientId()); - RecordId id = reader.readHeadRecordId(); + String id = reader.readHeadRecordId(); if (id == null) { log.debug("Head not found, discarding request from client {}", msg.getClientId()); Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetSegmentRequestHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetSegmentRequestHandler.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/GetSegmentRequestHandler.java (working copy) @@ -39,14 +39,18 @@ protected void channelRead0(ChannelHandlerContext ctx, GetSegmentRequest msg) throws Exception { log.debug("Reading segment {} for client {}", msg.getSegmentId(), msg.getClientId()); - Segment segment = reader.readSegment(msg.getSegmentId()); + byte[] data = reader.readSegment(msg.getSegmentId()); - if (segment == null) { + if (data == null) { log.debug("Segment {} not found, discarding request from client {}", msg.getSegmentId(), msg.getClientId()); return; } - ctx.writeAndFlush(new GetSegmentResponse(msg.getClientId(), segment)); + ctx.writeAndFlush(new GetSegmentResponse(msg.getClientId(), msg.getSegmentId(), data)); } + private String getId(Segment segment) { + return segment.getSegmentId().asUUID().toString(); + } + } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/ResponseObserverHandler.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/ResponseObserverHandler.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/ResponseObserverHandler.java (working copy) @@ -48,11 +48,11 @@ } private void onGetSegmentResponse(GetSegmentResponse response) { - observer.didSendSegmentBytes(response.getClientId(), response.getSegment().size()); + observer.didSendSegmentBytes(response.getClientId(), response.getSegmentData().length); } private void onGetBlobResponse(GetBlobResponse response) { - observer.didSendBinariesBytes(response.getClientId(), (int) Math.max(0, response.getBlob().length())); + observer.didSendBinariesBytes(response.getClientId(), (int) Math.max(0, response.getBlobData().length)); } } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyBlobReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyBlobReader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyBlobReader.java (working copy) @@ -17,10 +17,8 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import org.apache.jackrabbit.oak.api.Blob; - interface StandbyBlobReader { - Blob readBlob(String blobId); + byte[] readBlob(String blobId); } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyHeadReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyHeadReader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyHeadReader.java (working copy) @@ -17,8 +17,6 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import org.apache.jackrabbit.oak.segment.RecordId; - /** * Read the head record ID. */ @@ -30,6 +28,6 @@ * @return the head record ID or {@code null} if the head record ID can't be * found. */ - RecordId readHeadRecordId(); + String readHeadRecordId(); } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbySegmentReader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbySegmentReader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbySegmentReader.java (working copy) @@ -17,12 +17,8 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import java.util.UUID; - -import org.apache.jackrabbit.oak.segment.Segment; - interface StandbySegmentReader { - Segment readSegment(UUID segmentId); + byte[] readSegment(String segmentId); } Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/server/StandbyServer.java (working copy) @@ -46,12 +46,10 @@ import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.util.CharsetUtil; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobRequestDecoder; import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobResponseEncoder; -import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadRequestDecoder; import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponseEncoder; -import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequestDecoder; import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponseEncoder; +import org.apache.jackrabbit.oak.segment.standby.codec.RequestDecoder; import org.apache.jackrabbit.oak.segment.standby.jmx.StandbyStatusMBean; import org.apache.jackrabbit.oak.segment.standby.store.CommunicationObserver; import org.slf4j.Logger; @@ -131,9 +129,7 @@ p.addLast(new LineBasedFrameDecoder(8192)); p.addLast(new StringDecoder(CharsetUtil.UTF_8)); - p.addLast(new GetHeadRequestDecoder()); - p.addLast(new GetSegmentRequestDecoder()); - p.addLast(new GetBlobRequestDecoder()); + p.addLast(new RequestDecoder()); p.addLast(new StateHandler(newStateConsumer())); p.addLast(new RequestObserverHandler(observer)); Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/store/RemoteSegmentLoader.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/store/RemoteSegmentLoader.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/store/RemoteSegmentLoader.java (working copy) @@ -1,34 +0,0 @@ -/* - * 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.segment.standby.store; - -import org.apache.jackrabbit.oak.api.Blob; -import org.apache.jackrabbit.oak.segment.Segment; - -public interface RemoteSegmentLoader { - - Segment readSegment(String id); - - Blob readBlob(String blobId); - - void close(); - - boolean isClosed(); - - boolean isRunning(); - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStore.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStore.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStore.java (working copy) @@ -1,280 +0,0 @@ -/* - * 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.segment.standby.store; - -import static com.google.common.collect.Sets.newHashSet; -import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; - -import javax.annotation.Nonnull; - -import org.apache.jackrabbit.oak.segment.RecordId; -import org.apache.jackrabbit.oak.segment.Segment; -import org.apache.jackrabbit.oak.segment.SegmentId; -import org.apache.jackrabbit.oak.segment.SegmentNodeState; -import org.apache.jackrabbit.oak.segment.SegmentStore; -import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.spi.blob.BlobStore; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class StandbyStore implements SegmentStore, Closeable { - - private static final Logger log = LoggerFactory.getLogger(StandbyStore.class); - - private final FileStore delegate; - - private RemoteSegmentLoader loader; - - public StandbyStore(FileStore delegate) { - this.delegate = delegate; - } - - @Nonnull - @Override - public SegmentId newSegmentId(long msb, long lsb) { - return new SegmentId(this, msb, lsb); - } - - @Nonnull - @Override - public SegmentId newBulkSegmentId() { - SegmentId segmentId = delegate.newBulkSegmentId(); - long msb = segmentId.getMostSignificantBits(); - long lsb = segmentId.getLeastSignificantBits(); - return newSegmentId(msb, lsb); - } - - @Nonnull - @Override - public SegmentId newDataSegmentId() { - SegmentId segmentId = delegate.newDataSegmentId(); - long msb = segmentId.getMostSignificantBits(); - long lsb = segmentId.getLeastSignificantBits(); - return newSegmentId(msb, lsb); - } - - @Override - public boolean containsSegment(SegmentId id) { - return delegate.containsSegment(id); - } - - private List getReferencedIds(Segment s) { - List segmentIds = new ArrayList<>(); - - for (int i = 0; i < s.getReferencedSegmentIdCount(); i++) { - UUID uuid = s.getReferencedSegmentId(i); - long msb = uuid.getMostSignificantBits(); - long lsb = uuid.getLeastSignificantBits(); - segmentIds.add(delegate.newSegmentId(msb, lsb)); - } - - return segmentIds; - } - - @Override - public Segment readSegment(SegmentId sid) { - callId++; - Deque ids = new ArrayDeque(); - ids.offer(sid); - int err = 0; - Set persisted = new HashSet(); - - Map cache = new HashMap(); - long cacheOps = 0; - - long cacheWeight = 0; - long maxWeight = 0; - long maxKeys = 0; - - Set visited = newHashSet(); - - while (!ids.isEmpty()) { - SegmentId id = ids.remove(); - - visited.add(id); - - if (!persisted.contains(id) && !delegate.containsSegment(id)) { - Segment s; - boolean logRefs = true; - if (cache.containsKey(id)) { - s = cache.remove(id); - cacheWeight -= s.size(); - cacheOps++; - logRefs = false; - } else { - log.debug("transferring segment {}", id); - s = loader.readSegment(id.toString()); - } - - if (s != null) { - log.debug("processing segment {} with size {}", id, - s.size()); - if (id.isDataSegmentId()) { - boolean hasPendingRefs = false; - List refs = getReferencedIds(s); - if (logRefs) { - log.debug("{} -> {}", id, refs); - } - for (SegmentId nr : refs) { - // skip already persisted or self-ref - if (persisted.contains(nr) || id.equals(nr) || visited.contains(nr)) { - continue; - } - hasPendingRefs = true; - if (!ids.contains(nr)) { - if (nr.isBulkSegmentId()) { - // binaries first - ids.addFirst(nr); - } else { - // data segments last - ids.add(nr); - } - } - } - - if (!hasPendingRefs) { - persisted.add(id); - persist(id, s); - } else { - // persist it later, after the refs are in place - ids.add(id); - - // TODO there is a chance this might introduce - // a OOME because of the position of the current - // segment in the processing queue. putting it at - // the end of the current queue means it will stay - // in the cache until the pending queue of the - // segment's references is processed. - cache.put(id, s); - cacheWeight += s.size(); - cacheOps++; - - maxWeight = Math.max(maxWeight, cacheWeight); - maxKeys = Math.max(maxKeys, cache.size()); - } - } else { - persisted.add(id); - persist(id, s); - } - ids.removeAll(persisted); - err = 0; - } else { - log.error("could NOT read segment {}", id); - if (loader.isClosed() || err == 4) { - loader.close(); - throw new IllegalStateException( - "Unable to load remote segment " + id); - } - err++; - ids.addFirst(id); - } - } else { - persisted.add(id); - } - } - cacheStats.put(callId, "W: " + humanReadableByteCount(maxWeight) - + ", Keys: " + maxKeys + ", Ops: " + cacheOps); - return delegate.readSegment(sid); - } - - public void persist(SegmentId in, Segment s) { - SegmentId id = delegate.newSegmentId(in.getMostSignificantBits(), in.getLeastSignificantBits()); - log.debug("persisting segment {} with size {}", id, s.size()); - try { - ByteArrayOutputStream bout = new ByteArrayOutputStream(s.size()); - s.writeTo(bout); - writeSegment(id, bout.toByteArray(), 0, s.size()); - } catch (IOException e) { - throw new IllegalStateException("Unable to write remote segment " - + id, e); - } - } - - private long callId = 0; - private Map cacheStats; - - public void preSync(RemoteSegmentLoader loader) { - this.loader = loader; - this.cacheStats = new HashMap(); - } - - public void postSync() { - loader = null; - if (log.isDebugEnabled() && !cacheStats.isEmpty()) { - log.debug("sync cache stats {}", cacheStats); - } - cacheStats = null; - } - - @Override - public void writeSegment(SegmentId id, byte[] bytes, int offset, int length) throws IOException { - delegate.writeSegment(id, bytes, offset, length); - } - - @Override - public void close() { - delegate.close(); - } - - public long size() { - return delegate.getStats().getApproximateSize(); - } - - public void cleanup() { - try { - delegate.flush(); - } catch (IOException e) { - log.error("Error running cleanup", e); - } - } - - public SegmentNodeState getHead() { - return delegate.getHead(); - } - - public SegmentNodeState newSegmentNodeState(RecordId id) { - return delegate.getReader().readNode(id); - } - - public boolean setHead(@Nonnull SegmentNodeState expected, @Nonnull SegmentNodeState head) { - return delegate.getRevisions().setHead(expected.getRecordId(), head.getRecordId()); - } - - public Segment newSegment(SegmentId segmentId, ByteBuffer buffer) { - return new Segment(this, delegate.getReader(), segmentId, buffer); - } - - public BlobStore getBlobStore() { - return delegate.getBlobStore(); - } - -} Index: src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java =================================================================== --- src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java (revision 1760674) +++ src/main/java/org/apache/jackrabbit/oak/segment/standby/store/StandbyStoreService.java (working copy) @@ -38,7 +38,7 @@ import org.apache.jackrabbit.oak.segment.SegmentStore; import org.apache.jackrabbit.oak.segment.SegmentStoreProvider; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; @@ -95,8 +95,9 @@ private FileStore fileStore; private StandbyServer primary = null; - private StandbyClient sync = null; + private StandbySync sync = null; + private ServiceRegistration syncReg = null; @Activate @@ -157,7 +158,7 @@ int readTimeout = PropertiesUtil.toInteger(props.get(READ_TIMEOUT), READ_TIMEOUT_DEFAULT); boolean clean = PropertiesUtil.toBoolean(props.get(AUTO_CLEAN), AUTO_CLEAN_DEFAULT); - sync = new StandbyClient(host, port, fileStore, secure, readTimeout, clean); + sync = new StandbySync(host, port, fileStore, secure, readTimeout, clean); Dictionary dictionary = new Hashtable(); dictionary.put("scheduler.period", interval); dictionary.put("scheduler.concurrent", false); Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/BrokenNetworkTest.java (working copy) @@ -25,7 +25,7 @@ import org.apache.jackrabbit.oak.segment.NetworkErrorProxy; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; @@ -126,7 +126,7 @@ addTestContent(store, "server"); storeS.flush(); // this speeds up the test a little bit... - StandbyClient cl = newStandbyClient(storeC, proxyPort, ssl); + StandbySync cl = newStandbyClient(storeC, proxyPort, ssl); cl.run(); try { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/DataStoreTestBase.java (working copy) @@ -42,7 +42,7 @@ import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; import org.apache.jackrabbit.oak.segment.file.FileStore; import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; @@ -112,7 +112,7 @@ byte[] data = addTestContent(store, "server", blobSize); primary.flush(); - StandbyClient cl = newStandbyClient(secondary); + StandbySync cl = newStandbyClient(secondary); cl.run(); try { @@ -188,7 +188,7 @@ byte[] data = addTestContent(store, "server", blobSize); primary.flush(); - StandbyClient cl = newStandbyClient(secondary, proxyPort); + StandbySync cl = newStandbyClient(secondary, proxyPort); cl.run(); try { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverIPRangeTest.java (working copy) @@ -24,7 +24,7 @@ import static org.junit.Assert.assertFalse; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; @@ -146,7 +146,7 @@ addTestContent(store, "server"); storeS.flush(); // this speeds up the test a little bit... - StandbyClient cl = new StandbyClient(host, port, storeC, false, timeout, false); + StandbySync cl = new StandbySync(host, port, storeC, false, timeout, false); cl.run(); try { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverMultipleClientsTestIT.java (working copy) @@ -23,7 +23,7 @@ import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; import org.apache.jackrabbit.oak.segment.SegmentTestUtils; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; @@ -50,8 +50,8 @@ SegmentTestUtils.addTestContent(store, "server"); storeS.flush(); // this speeds up the test a little bit... - StandbyClient cl1 = newStandbyClient(storeC); - StandbyClient cl2 = newStandbyClient(storeC2); + StandbySync cl1 = newStandbyClient(storeC); + StandbySync cl2 = newStandbyClient(storeC2); try { assertFalse("first client has invalid initial store!", storeS.getHead().equals(storeC.getHead())); Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/FailoverSslTestIT.java (working copy) @@ -24,7 +24,7 @@ import static org.junit.Assert.assertFalse; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; @@ -52,7 +52,7 @@ addTestContent(store, "server"); storeS.flush(); // this speeds up the test a little bit... - StandbyClient cl = newStandbyClient(storeC, port, true); + StandbySync cl = newStandbyClient(storeC, port, true); cl.run(); try { @@ -72,7 +72,7 @@ addTestContent(store, "server"); storeS.flush(); // this speeds up the test a little bit... - StandbyClient cl = newStandbyClient(storeC); + StandbySync cl = newStandbyClient(storeC); cl.run(); try { @@ -92,7 +92,7 @@ addTestContent(store, "server"); storeS.flush(); // this speeds up the test a little bit... - StandbyClient cl = newStandbyClient(storeC, port, true); + StandbySync cl = newStandbyClient(storeC, port, true); cl.run(); try { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/MBeanTest.java (working copy) @@ -30,7 +30,7 @@ import javax.management.MBeanServer; import javax.management.ObjectName; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.jmx.StandbyStatusMBean; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.junit.After; @@ -86,7 +86,7 @@ @Test public void testClientEmptyConfigNoServer() throws Exception { - final StandbyClient client = newStandbyClient(storeC); + final StandbySync client = newStandbyClient(storeC); client.start(); client.run(); @@ -123,8 +123,8 @@ @Test public void testClientNoServer() throws Exception { - System.setProperty(StandbyClient.CLIENT_ID_PROPERTY_NAME, "Foo"); - final StandbyClient client = newStandbyClient(storeC); + System.setProperty(StandbySync.CLIENT_ID_PROPERTY_NAME, "Foo"); + final StandbySync client = newStandbyClient(storeC); client.start(); client.run(); @@ -151,8 +151,8 @@ final StandbyServer server = new StandbyServer(port, this.storeS); server.start(); - System.setProperty(StandbyClient.CLIENT_ID_PROPERTY_NAME, "Bar"); - final StandbyClient client = newStandbyClient(storeC); + System.setProperty(StandbySync.CLIENT_ID_PROPERTY_NAME, "Bar"); + final StandbySync client = newStandbyClient(storeC); client.start(); client.run(); Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/RecoverTestIT.java (working copy) @@ -24,7 +24,7 @@ import static org.junit.Assert.assertFalse; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; @@ -55,7 +55,7 @@ addTestContent(store, "server"); storeS.flush(); - StandbyClient cl = newStandbyClient(storeC); + StandbySync cl = newStandbyClient(storeC); try { assertFalse("stores are not expected to be equal", storeS.getHead().equals(storeC.getHead())); cl.run(); Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTest.java (working copy) @@ -28,13 +28,14 @@ import java.io.IOException; import java.util.Random; +import com.google.common.io.ByteStreams; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; @@ -44,8 +45,6 @@ import org.junit.Before; import org.junit.Test; -import com.google.common.io.ByteStreams; - public class StandbyTest extends TestBase { @Before @@ -71,7 +70,7 @@ byte[] data = addTestContent(store, "server", blobSize, 150); primary.flush(); - StandbyClient cl = newStandbyClient(secondary); + StandbySync cl = newStandbyClient(secondary); cl.run(); try { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/StandbyTestIT.java (working copy) @@ -35,7 +35,7 @@ import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; @@ -96,7 +96,7 @@ byte[] data = addTestContent(store, "server", blobSize, dataNodes); primary.flush(); - StandbyClient cl = newStandbyClient(secondary); + StandbySync cl = newStandbyClient(secondary); try { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/TestBase.java (working copy) @@ -30,7 +30,7 @@ import org.apache.jackrabbit.oak.commons.CIHelper; import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.stats.DefaultStatisticsProvider; import org.junit.BeforeClass; import org.junit.Rule; @@ -149,18 +149,18 @@ return timeout; } - public StandbyClient newStandbyClient(FileStore store) throws Exception { + public StandbySync newStandbyClient(FileStore store) throws Exception { return newStandbyClient(store, port, false); } - public StandbyClient newStandbyClient(FileStore store, int port) + public StandbySync newStandbyClient(FileStore store, int port) throws Exception { return newStandbyClient(store, port, false); } - public StandbyClient newStandbyClient(FileStore store, int port, + public StandbySync newStandbyClient(FileStore store, int port, boolean secure) throws Exception { - return new StandbyClient(LOCALHOST, port, store, secure, timeout, false); + return new StandbySync(LOCALHOST, port, store, secure, timeout, false); } } Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BenchmarkBase.java (working copy) @@ -29,7 +29,7 @@ import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser; import org.apache.jackrabbit.oak.segment.file.FileStore; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.stats.DefaultStatisticsProvider; public class BenchmarkBase { @@ -97,16 +97,16 @@ return newFileStore(directory, executor); } - public StandbyClient newStandbyClient(FileStore store) throws Exception { + public StandbySync newStandbyClient(FileStore store) throws Exception { return newStandbyClient(store, port, false); } - public StandbyClient newStandbyClient(FileStore store, int port) throws Exception { + public StandbySync newStandbyClient(FileStore store, int port) throws Exception { return newStandbyClient(store, port, false); } - public StandbyClient newStandbyClient(FileStore store, int port, boolean secure) throws Exception { - return new StandbyClient(LOCALHOST, port, store, secure, timeout, false); + public StandbySync newStandbyClient(FileStore store, int port, boolean secure) throws Exception { + return new StandbySync(LOCALHOST, port, store, secure, timeout, false); } private static File createTmpTargetDir(String name) throws IOException { Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BulkTransferBenchmark.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BulkTransferBenchmark.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/benchmark/BulkTransferBenchmark.java (working copy) @@ -27,7 +27,7 @@ import javax.management.ObjectName; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; -import org.apache.jackrabbit.oak.segment.standby.client.StandbyClient; +import org.apache.jackrabbit.oak.segment.standby.client.StandbySync; import org.apache.jackrabbit.oak.segment.standby.jmx.StandbyStatusMBean; import org.apache.jackrabbit.oak.segment.standby.server.StandbyServer; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; @@ -94,8 +94,8 @@ final StandbyServer server = new StandbyServer(port, storeS, useSSL); server.start(); - System.setProperty(StandbyClient.CLIENT_ID_PROPERTY_NAME, "Bar"); - StandbyClient cl = newStandbyClient(storeC, port, useSSL); + System.setProperty(StandbySync.CLIENT_ID_PROPERTY_NAME, "Bar"); + StandbySync cl = newStandbyClient(storeC, port, useSSL); final MBeanServer jmxServer = ManagementFactory.getPlatformMBeanServer(); ObjectName status = new ObjectName(StandbyStatusMBean.JMX_NAME + ",id=*"); Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestDecoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestDecoderTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobRequestDecoderTest.java (working copy) @@ -1,43 +0,0 @@ -/* - * 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.segment.standby.codec; - -import static org.junit.Assert.assertEquals; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.Test; - -public class GetBlobRequestDecoderTest { - - @Test - public void shouldDecodeValidMessages() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new GetBlobRequestDecoder()); - channel.writeInbound(Messages.newGetBlobRequest("clientId", "blobId", false)); - GetBlobRequest request = (GetBlobRequest) channel.readInbound(); - assertEquals("clientId", request.getClientId()); - assertEquals("blobId", request.getBlobId()); - } - - @Test - public void shouldIgnoreInvalidMessages() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new GetBlobRequestDecoder()); - channel.writeInbound("Standby-CMD@clientId:z"); - assertEquals("Standby-CMD@clientId:z", channel.readInbound()); - } - -} Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponseEncoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponseEncoderTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetBlobResponseEncoderTest.java (working copy) @@ -19,42 +19,33 @@ import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.hash; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import java.io.ByteArrayInputStream; - import com.google.common.base.Charsets; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.jackrabbit.oak.api.Blob; import org.junit.Test; public class GetBlobResponseEncoderTest { @Test public void encodeResponse() throws Exception { - byte[] data = new byte[] {1, 2, 3}; + byte[] blobData = new byte[] {1, 2, 3}; - String contentIdentity = "contentIdentity"; - byte[] contentIdentityBytes = contentIdentity.getBytes(Charsets.UTF_8); + String blobId = "blobId"; + byte[] blobIdBytes = blobId.getBytes(Charsets.UTF_8); - Blob blob = mock(Blob.class); - when(blob.getNewStream()).thenReturn(new ByteArrayInputStream(data)); - when(blob.getContentIdentity()).thenReturn(contentIdentity); - EmbeddedChannel channel = new EmbeddedChannel(new GetBlobResponseEncoder()); - channel.writeOutbound(new GetBlobResponse("clientId", blob)); + channel.writeOutbound(new GetBlobResponse("clientId", blobId, blobData)); ByteBuf buffer = (ByteBuf) channel.readOutbound(); ByteBuf expected = Unpooled.buffer(); - expected.writeInt(3); + expected.writeInt(1 + 4 + blobIdBytes.length + 8 + blobData.length); expected.writeByte(Messages.HEADER_BLOB); - expected.writeInt(contentIdentityBytes.length); - expected.writeBytes(contentIdentityBytes); - expected.writeLong(hash(data)); - expected.writeBytes(data); + expected.writeInt(blobIdBytes.length); + expected.writeBytes(blobIdBytes); + expected.writeLong(hash(blobData)); + expected.writeBytes(blobData); assertEquals(expected, buffer); } Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestDecoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestDecoderTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadRequestDecoderTest.java (working copy) @@ -1,42 +0,0 @@ -/* - * 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.segment.standby.codec; - -import static org.junit.Assert.assertEquals; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.Test; - -public class GetHeadRequestDecoderTest { - - @Test - public void shouldDecodeValidMessages() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new GetHeadRequestDecoder()); - channel.writeInbound(Messages.newGetHeadRequest("clientId", false)); - GetHeadRequest request = (GetHeadRequest) channel.readInbound(); - assertEquals("clientId", request.getClientId()); - } - - @Test - public void shouldIgnoreInvalidMessages() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new GetHeadRequestDecoder()); - channel.writeInbound("Standby-CMD@clientId:z"); - assertEquals("Standby-CMD@clientId:z", channel.readInbound()); - } - -} Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponseEncoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponseEncoderTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetHeadResponseEncoderTest.java (working copy) @@ -17,30 +17,29 @@ package org.apache.jackrabbit.oak.segment.standby.codec; -import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.mockRecordId; import static org.junit.Assert.assertEquals; import com.google.common.base.Charsets; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.jackrabbit.oak.segment.RecordId; import org.junit.Test; public class GetHeadResponseEncoderTest { @Test public void encodeResponse() throws Exception { - RecordId recordId = mockRecordId(1, 2, 8); + String recordId = "recordId"; + byte[] recordIdBytes = recordId.getBytes(Charsets.UTF_8); EmbeddedChannel channel = new EmbeddedChannel(new GetHeadResponseEncoder()); - channel.writeOutbound(new GetHeadResponse("clientId", recordId)); + channel.writeOutbound(new GetHeadResponse("clientId", "recordId")); ByteBuf buffer = (ByteBuf) channel.readOutbound(); ByteBuf expected = Unpooled.buffer(); - expected.writeInt(recordId.toString().getBytes(Charsets.UTF_8).length + 1); + expected.writeInt(recordIdBytes.length + 1); expected.writeByte(Messages.HEADER_RECORD); - expected.writeBytes(recordId.toString().getBytes(Charsets.UTF_8)); + expected.writeBytes(recordIdBytes); assertEquals(expected, buffer); } Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestDecoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestDecoderTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentRequestDecoderTest.java (working copy) @@ -1,45 +0,0 @@ -/* - * 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.segment.standby.codec; - -import static org.junit.Assert.assertEquals; - -import java.util.UUID; - -import io.netty.channel.embedded.EmbeddedChannel; -import org.junit.Test; - -public class GetSegmentRequestDecoderTest { - - @Test - public void shouldDecodeValidMessages() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new GetSegmentRequestDecoder()); - channel.writeInbound(Messages.newGetSegmentRequest("clientId", new UUID(1, 2).toString(), false)); - GetSegmentRequest request = (GetSegmentRequest) channel.readInbound(); - assertEquals("clientId", request.getClientId()); - assertEquals(new UUID(1, 2), request.getSegmentId()); - } - - @Test - public void shouldIgnoreInvalidMessages() throws Exception { - EmbeddedChannel channel = new EmbeddedChannel(new GetSegmentRequestDecoder()); - channel.writeInbound("Standby-CMD@clientId:z"); - assertEquals("Standby-CMD@clientId:z", channel.readInbound()); - } - -} Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponseEncoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponseEncoderTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/GetSegmentResponseEncoderTest.java (working copy) @@ -18,7 +18,6 @@ package org.apache.jackrabbit.oak.segment.standby.codec; import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.hash; -import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.mockSegment; import static org.junit.Assert.assertEquals; import java.util.UUID; @@ -26,7 +25,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.jackrabbit.oak.segment.Segment; import org.junit.Test; public class GetSegmentResponseEncoderTest { @@ -35,10 +33,9 @@ public void encodeResponse() throws Exception { UUID uuid = new UUID(1, 2); byte[] data = new byte[] {3, 4, 5}; - Segment segment = mockSegment(uuid, data); EmbeddedChannel channel = new EmbeddedChannel(new GetSegmentResponseEncoder()); - channel.writeOutbound(new GetSegmentResponse("clientId", segment)); + channel.writeOutbound(new GetSegmentResponse("clientId", uuid.toString(), data)); ByteBuf buffer = (ByteBuf) channel.readOutbound(); ByteBuf expected = Unpooled.buffer(); Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoderTest.java (revision 0) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoderTest.java (working copy) @@ -0,0 +1,61 @@ +/* + * 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.segment.standby.codec; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Test; + +public class RequestDecoderTest { + + @Test + public void shouldDecodeValidGetBlobRequests() throws Exception { + EmbeddedChannel channel = new EmbeddedChannel(new RequestDecoder()); + channel.writeInbound(Messages.newGetBlobRequest("clientId", "blobId", false)); + GetBlobRequest request = (GetBlobRequest) channel.readInbound(); + assertEquals("clientId", request.getClientId()); + assertEquals("blobId", request.getBlobId()); + } + + @Test + public void shouldDecodeValidGetHeadRequests() throws Exception { + EmbeddedChannel channel = new EmbeddedChannel(new RequestDecoder()); + channel.writeInbound(Messages.newGetHeadRequest("clientId", false)); + GetHeadRequest request = (GetHeadRequest) channel.readInbound(); + assertEquals("clientId", request.getClientId()); + } + + @Test + public void shouldDecodeValidGetSegmentRequests() throws Exception { + EmbeddedChannel channel = new EmbeddedChannel(new RequestDecoder()); + channel.writeInbound(Messages.newGetSegmentRequest("clientId", "segmentId", false)); + GetSegmentRequest request = (GetSegmentRequest) channel.readInbound(); + assertEquals("clientId", request.getClientId()); + assertEquals("segmentId", request.getSegmentId()); + } + + @Test + public void shouldDropInvalidMessages() throws Exception { + EmbeddedChannel channel = new EmbeddedChannel(new RequestDecoder()); + channel.writeInbound("Standby-CMD@clientId:z"); + assertNull(channel.readInbound()); + } + +} Property changes on: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/RequestDecoderTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoderTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoderTest.java (revision 0) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoderTest.java (working copy) @@ -0,0 +1,142 @@ +/* + * 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.segment.standby.codec; + +import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.hash; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.util.UUID; + +import com.google.common.base.Charsets; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Test; + +public class ResponseDecoderTest { + + @Test + public void unrecognizedMessagesShouldBeDropped() throws Exception { + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(1); + buf.writeByte(-1); + + EmbeddedChannel channel = new EmbeddedChannel(new ResponseDecoder()); + channel.writeInbound(buf); + assertNull(channel.readInbound()); + } + + @Test + public void shouldDecodeValidGetBlobResponses() throws Exception { + byte[] blobData = new byte[] {1, 2, 3}; + + String blobId = "blobId"; + byte[] blobIdBytes = blobId.getBytes(Charsets.UTF_8); + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(1 + 4 + blobIdBytes.length + 8 + blobData.length); + buf.writeByte(Messages.HEADER_BLOB); + buf.writeInt(blobIdBytes.length); + buf.writeBytes(blobIdBytes); + buf.writeLong(hash(blobData)); + buf.writeBytes(blobData); + + EmbeddedChannel channel = new EmbeddedChannel(new ResponseDecoder()); + channel.writeInbound(buf); + GetBlobResponse response = (GetBlobResponse) channel.readInbound(); + assertEquals("blobId", response.getBlobId()); + assertArrayEquals(blobData, response.getBlobData()); + } + + @Test + public void shouldDropInvalidGetBlobResponses() throws Exception { + byte[] blobData = new byte[] {1, 2, 3}; + + String blobId = "blobId"; + byte[] blobIdBytes = blobId.getBytes(Charsets.UTF_8); + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(1 + 4 + blobIdBytes.length + 8 + blobData.length); + buf.writeByte(Messages.HEADER_BLOB); + buf.writeInt(blobIdBytes.length); + buf.writeBytes(blobIdBytes); + buf.writeLong(hash(blobData) + 1); + buf.writeBytes(blobData); + + EmbeddedChannel channel = new EmbeddedChannel(new ResponseDecoder()); + channel.writeInbound(buf); + assertNull(channel.readInbound()); + } + + @Test + public void shouldDecodeValidGetHeadResponses() throws Exception { + String recordId = "recordId"; + byte[] recordIdBytes = recordId.getBytes(Charsets.UTF_8); + + ByteBuf in = Unpooled.buffer(); + in.writeInt(recordIdBytes.length + 1); + in.writeByte(Messages.HEADER_RECORD); + in.writeBytes(recordIdBytes); + + EmbeddedChannel channel = new EmbeddedChannel(new ResponseDecoder()); + channel.writeInbound(in); + GetHeadResponse response = (GetHeadResponse) channel.readInbound(); + assertEquals(recordId, response.getHeadRecordId()); + } + + @Test + public void shouldDecodeValidGetSegmentResponses() throws Exception { + UUID uuid = new UUID(1, 2); + byte[] data = new byte[] {3, 4, 5}; + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(data.length + 25); + buf.writeByte(Messages.HEADER_SEGMENT); + buf.writeLong(uuid.getMostSignificantBits()); + buf.writeLong(uuid.getLeastSignificantBits()); + buf.writeLong(hash(data)); + buf.writeBytes(data); + + EmbeddedChannel channel = new EmbeddedChannel(new ResponseDecoder()); + channel.writeInbound(buf); + GetSegmentResponse response = (GetSegmentResponse) channel.readInbound(); + assertEquals(uuid, UUID.fromString(response.getSegmentId())); + assertArrayEquals(data, response.getSegmentData()); + } + + @Test + public void shouldDropInvalidGetSegmentResponses() throws Exception { + UUID uuid = new UUID(1, 2); + byte[] data = new byte[] {3, 4, 5}; + + ByteBuf buf = Unpooled.buffer(); + buf.writeInt(data.length + 25); + buf.writeByte(Messages.HEADER_SEGMENT); + buf.writeLong(uuid.getMostSignificantBits()); + buf.writeLong(uuid.getLeastSignificantBits()); + buf.writeLong(hash(data) + 1); + buf.writeBytes(data); + + EmbeddedChannel channel = new EmbeddedChannel(new ResponseDecoder()); + channel.writeInbound(buf); + assertNull(channel.readInbound()); + } + +} Property changes on: src/test/java/org/apache/jackrabbit/oak/segment/standby/codec/ResponseDecoderTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetBlobRequestHandlerTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetBlobRequestHandlerTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetBlobRequestHandlerTest.java (working copy) @@ -17,14 +17,13 @@ package org.apache.jackrabbit.oak.segment.standby.server; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobRequest; import org.apache.jackrabbit.oak.segment.standby.codec.GetBlobResponse; import org.junit.Test; @@ -33,16 +32,17 @@ @Test public void successfulReadsShouldGenerateResponses() throws Exception { - Blob blob = mock(Blob.class); + byte[] blobData = new byte[] {99, 114, 97, 112}; StandbyBlobReader reader = mock(StandbyBlobReader.class); - when(reader.readBlob("blobId")).thenReturn(blob); + when(reader.readBlob("blobId")).thenReturn(blobData); EmbeddedChannel channel = new EmbeddedChannel(new GetBlobRequestHandler(reader)); channel.writeInbound(new GetBlobRequest("clientId", "blobId")); GetBlobResponse response = (GetBlobResponse) channel.readOutbound(); assertEquals("clientId", response.getClientId()); - assertSame(blob, response.getBlob()); + assertEquals("blobId", response.getBlobId()); + assertArrayEquals(blobData, response.getBlobData()); } @Test Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetHeadRequestHandlerTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetHeadRequestHandlerTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetHeadRequestHandlerTest.java (working copy) @@ -17,15 +17,12 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.mockRecordId; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.jackrabbit.oak.segment.RecordId; import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadRequest; import org.apache.jackrabbit.oak.segment.standby.codec.GetHeadResponse; import org.junit.Test; @@ -34,15 +31,13 @@ @Test public void successfulReadsShouldGenerateResponses() throws Exception { - RecordId headRecordId = mockRecordId(1, 2, 8); - StandbyHeadReader reader = mock(StandbyHeadReader.class); - when(reader.readHeadRecordId()).thenReturn(headRecordId); + when(reader.readHeadRecordId()).thenReturn("recordId"); EmbeddedChannel channel = new EmbeddedChannel(new GetHeadRequestHandler(reader)); channel.writeInbound(new GetHeadRequest("clientId")); GetHeadResponse response = (GetHeadResponse) channel.readOutbound(); - assertSame(headRecordId, response.getHeadRecordId()); + assertEquals("recordId", response.getHeadRecordId()); assertEquals("clientId", response.getClientId()); } Index: src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetSegmentRequestHandlerTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetSegmentRequestHandlerTest.java (revision 1760674) +++ src/test/java/org/apache/jackrabbit/oak/segment/standby/server/GetSegmentRequestHandlerTest.java (working copy) @@ -17,17 +17,15 @@ package org.apache.jackrabbit.oak.segment.standby.server; -import static org.apache.jackrabbit.oak.segment.standby.StandbyTestUtils.mockSegment; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.UUID; import io.netty.channel.embedded.EmbeddedChannel; -import org.apache.jackrabbit.oak.segment.Segment; import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentRequest; import org.apache.jackrabbit.oak.segment.standby.codec.GetSegmentResponse; import org.junit.Test; @@ -36,18 +34,17 @@ @Test public void successfulReadsShouldGenerateResponses() throws Exception { - UUID uuid = new UUID(1, 2); + byte[] data = new byte[] {3, 4, 5}; - Segment segment = mockSegment(uuid, new byte[] {3, 4, 5}); - StandbySegmentReader reader = mock(StandbySegmentReader.class); - when(reader.readSegment(uuid)).thenReturn(segment); + when(reader.readSegment("segmentId")).thenReturn(data); EmbeddedChannel channel = new EmbeddedChannel(new GetSegmentRequestHandler(reader)); - channel.writeInbound(new GetSegmentRequest("clientId", uuid)); + channel.writeInbound(new GetSegmentRequest("clientId", "segmentId")); GetSegmentResponse response = (GetSegmentResponse) channel.readOutbound(); assertEquals("clientId", response.getClientId()); - assertSame(segment, response.getSegment()); + assertEquals("segmentId", response.getSegmentId()); + assertArrayEquals(data, response.getSegmentData()); } @Test @@ -55,10 +52,10 @@ UUID uuid = new UUID(1, 2); StandbySegmentReader reader = mock(StandbySegmentReader.class); - when(reader.readSegment(uuid)).thenReturn(null); + when(reader.readSegment(uuid.toString())).thenReturn(null); EmbeddedChannel channel = new EmbeddedChannel(new GetSegmentRequestHandler(reader)); - channel.writeInbound(new GetSegmentRequest("clientId", uuid)); + channel.writeInbound(new GetSegmentRequest("clientId", uuid.toString())); assertNull(channel.readOutbound()); }