Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/GetTreeHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/GetTreeHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/GetTreeHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/GetTreeHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,264 @@ +/* + * 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.remote.http.handler; + +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.collect.Sets; +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; +import org.apache.jackrabbit.oak.remote.RemoteTree; +import org.apache.jackrabbit.oak.remote.RemoteTreeFilters; +import org.apache.jackrabbit.oak.remote.RemoteValue; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Map; +import java.util.Set; + +import static java.util.Collections.singletonMap; +import static org.apache.jackrabbit.oak.remote.http.handler.RemoteValues.renderJson; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendGone; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendInternalServerError; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendNotFound; + +abstract class GetTreeHandler implements Handler { + + protected abstract String readPath(HttpServletRequest request); + + protected abstract RemoteRevision readRevision(HttpServletRequest request, RemoteSession session); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + RemoteSession session = (RemoteSession) request.getAttribute("session"); + + if (session == null) { + sendInternalServerError(response, "session not found"); + return; + } + + RemoteRevision revision = readRevision(request, session); + + if (revision == null) { + sendGone(response, "unable to read the revision"); + return; + } + + RemoteTree tree = session.readTree(revision, readPath(request), readFilters(request)); + + if (tree == null) { + sendNotFound(response, singletonMap("Oak-Revision", revision.asString()), "tree not found"); + return; + } + + response.setStatus(HttpServletResponse.SC_OK); + response.setHeader("Oak-Revision", revision.asString()); + response.setContentType("application/json"); + + ServletOutputStream stream = response.getOutputStream(); + + JsonGenerator generator = new JsonFactory().createJsonGenerator(stream, JsonEncoding.UTF8); + renderResponse(generator, tree); + generator.flush(); + + stream.close(); + } + + private void renderResponse(JsonGenerator generator, RemoteTree tree) throws IOException { + if (tree == null) { + generator.writeNull(); + } else { + generator.writeStartObject(); + generator.writeFieldName("properties"); + renderProperties(generator, tree.getProperties()); + generator.writeFieldName("children"); + renderChildren(generator, tree.getChildren()); + generator.writeFieldName("hasMoreChildren"); + generator.writeBoolean(tree.hasMoreChildren()); + generator.writeEndObject(); + } + } + + private void renderChildren(JsonGenerator generator, Map<String, RemoteTree> children) throws IOException { + generator.writeStartObject(); + + for (Map.Entry<String, RemoteTree> entry : children.entrySet()) { + generator.writeFieldName(entry.getKey()); + renderResponse(generator, entry.getValue()); + } + + generator.writeEndObject(); + } + + private void renderProperties(JsonGenerator generator, Map<String, RemoteValue> properties) throws IOException { + generator.writeStartObject(); + + for (Map.Entry<String, RemoteValue> entry : properties.entrySet()) { + generator.writeFieldName(entry.getKey()); + renderJson(generator, entry.getValue()); + } + + generator.writeEndObject(); + } + + private RemoteTreeFilters readFilters(final HttpServletRequest request) { + return new RemoteTreeFilters() { + + @Override + public int getDepth() { + Integer depth = readDepth(request); + + if (depth == null) { + return super.getDepth(); + } + + return depth; + } + + @Override + public Set<String> getPropertyFilters() { + Set<String> propertyFilters = readPropertyFilters(request); + + if (propertyFilters == null) { + return super.getPropertyFilters(); + } + + return propertyFilters; + } + + @Override + public Set<String> getNodeFilters() { + Set<String> nodeFilters = readNodeFilters(request); + + if (nodeFilters == null) { + return super.getNodeFilters(); + } + + return nodeFilters; + } + + @Override + public long getBinaryThreshold() { + Long binaryThreshold = readBinaryThreshold(request); + + if (binaryThreshold == null) { + return super.getBinaryThreshold(); + } + + return binaryThreshold; + } + + @Override + public int getChildrenStart() { + Integer childrenStart = readChildrenStart(request); + + if (childrenStart == null) { + return super.getChildrenStart(); + } + + return childrenStart; + } + + @Override + public int getChildrenCount() { + Integer childrenCount = readChildrenCount(request); + + if (childrenCount == null) { + return super.getChildrenCount(); + } + + return childrenCount; + } + + }; + } + + private Integer readDepth(HttpServletRequest request) { + return readIntegerParameter(request, "depth"); + } + + private Set<String> readPropertyFilters(HttpServletRequest request) { + return readSetParameter(request, "properties"); + } + + private Set<String> readNodeFilters(HttpServletRequest request) { + return readSetParameter(request, "children"); + } + + private Long readBinaryThreshold(HttpServletRequest request) { + return readLongParameter(request, "binaries"); + } + + private Integer readChildrenStart(HttpServletRequest request) { + return readIntegerParameter(request, "childrenStart"); + } + + private Integer readChildrenCount(HttpServletRequest request) { + return readIntegerParameter(request, "childrenCount"); + } + + private Integer readIntegerParameter(HttpServletRequest request, String name) { + String value = request.getParameter(name); + + if (value == null) { + return null; + } + + Integer result; + + try { + result = Integer.parseInt(value, 10); + } catch (NumberFormatException e) { + result = null; + } + + return result; + } + + private Long readLongParameter(HttpServletRequest request, String name) { + String value = request.getParameter(name); + + if (value == null) { + return null; + } + + Long result; + + try { + result = Long.parseLong(value, 10); + } catch (NumberFormatException e) { + result = null; + } + + return result; + } + + private Set<String> readSetParameter(HttpServletRequest request, String name) { + String[] values = request.getParameterValues(name); + + if (values == null) { + return null; + } + + return Sets.newHashSet(values); + } + +}
Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,29 @@ +/* + * 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.remote.http.handler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public interface Handler { + + void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException; + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handlers.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handlers.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handlers.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/Handlers.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,157 @@ +/* + * 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.remote.http.handler; + +/** + * A collection of handlers used to respond to some requests handled by the + * remote servlet. + */ +public class Handlers { + + private Handlers() { + } + + /** + * Create an handler that will return the last revision available to the + * server. + * + * @return An instance of {@code Handler}. + */ + public static Handler createGetLastRevisionHandler() { + return withAuthentication(new GetLastRevisionHandler()); + } + + /** + * Create an handler that will return a repository sub-tree at a specific + * revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createGetRevisionTreeHandler() { + return withAuthentication(new GetRevisionTreeHandler()); + } + + /** + * Create an handler that will return a repository sub-tree at the latest + * known state. + * + * @return An instance of {@code Handler}. + */ + public static Handler createGetLastTreeHandler() { + return withAuthentication(new GetLastTreeHandler()); + } + + /** + * Create an handler that will return a 404 response to the client. + * + * @return An instance of {@code Handler}. + */ + public static Handler createNotFoundHandler() { + return new NotFoundHandler(); + } + + /** + * Create a handler that will read a binary object from the repository. + * + * @return An instance of {@code Handler}. + */ + public static Handler createGetBinaryHandler() { + return withAuthentication(new GetBinaryHandler()); + } + + /** + * Create a handler that will check if a binary exists + * + * @return An instance of {@code Handler} + */ + public static Handler createHeadBinaryHandler() { + return withAuthentication(new HeadBinaryHandler()); + } + + /** + * Create a handler that will perform the creation of new binary object. + * + * @return An instance of {@code Handler}. + */ + public static Handler createPostBinaryHandler() { + return withAuthentication(new PostBinaryHandler()); + } + + /** + * Create a handler that will patch the content at a specific revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createPatchSpecificRevisionHandler() { + return withAuthentication(new PatchSpecificRevisionHandler()); + } + + /** + * Create a handler that will patch the content at the last revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createPatchLastRevisionHandler() { + return withAuthentication(new PatchLastRevisionHandler()); + } + + /** + * Create a handler that checks if a tree exists at the last revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createHeadLastTreeHandler() { + return withAuthentication(new HeadLastTreeHandler()); + } + + /** + * Create a handler that checks if a tree exists at a given revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createHeadRevisionTreeHandler() { + return withAuthentication(new HeadRevisionTreeHandler()); + } + + /** + * Create a handler that searches for content at the last revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createSearchLastRevisionHandler() { + return withAuthentication(new SearchLastRevisionHandler()); + } + + /** + * Create a handler that searches for content at the provided revision. + * + * @return An instance of {@code Handler}. + */ + public static Handler createSearchSpecificRevisionHandler() { + return withAuthentication(new SearchSpecificRevisionHandler()); + } + + private static Handler withAuthentication(Handler authenticated) { + return new AuthenticationWrapperHandler(authenticated, createForbiddenHandler()); + } + + private static Handler createForbiddenHandler() { + return new UnauthorizedHandler(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadBinaryHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadBinaryHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadBinaryHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadBinaryHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,77 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteBinaryId; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendBadRequest; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendInternalServerError; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendNotFound; + +class HeadBinaryHandler implements Handler { + + private static final Pattern REQUEST_PATTERN = Pattern.compile("^/binaries/(.*)$"); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { + RemoteSession session = (RemoteSession) request.getAttribute("session"); + + if (session == null) { + sendInternalServerError(response, "session not found"); + return; + } + + String providedBinaryId = readBinaryId(request); + + if (providedBinaryId == null) { + sendBadRequest(response, "unable to read the provided binary ID"); + return; + } + + RemoteBinaryId binaryId = session.readBinaryId(providedBinaryId); + + if (binaryId == null) { + sendNotFound(response, "binary ID not found"); + return; + } + + response.setStatus(HttpServletResponse.SC_OK); + response.setHeader("Accept-Ranges", "bytes"); + } + + /** + * Extract binary id from request path and return it + */ + private String readBinaryId(HttpServletRequest request) { + Matcher matcher = REQUEST_PATTERN.matcher(request.getPathInfo()); + + if (matcher.matches()) { + return matcher.group(1); + } + + throw new IllegalStateException("handler bound at the wrong path"); + } +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadLastTreeHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadLastTreeHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadLastTreeHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadLastTreeHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,46 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class HeadLastTreeHandler extends HeadTreeHandler { + + private static final Pattern REQUEST_PATTERN = Pattern.compile("^/revisions/last/tree(/.*)$"); + + protected String readPath(HttpServletRequest request) { + Matcher matcher = REQUEST_PATTERN.matcher(request.getPathInfo()); + + if (matcher.matches()) { + return matcher.group(1); + } + + throw new IllegalStateException("handler bound at the wrong path"); + } + + @Override + protected RemoteRevision readRevision(HttpServletRequest request, RemoteSession session) { + return session.readLastRevision(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadRevisionTreeHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadRevisionTreeHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadRevisionTreeHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadRevisionTreeHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,53 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class HeadRevisionTreeHandler extends HeadTreeHandler { + + private static final Pattern REQUEST_PATTERN = Pattern.compile("^/revisions/([^/]+)/tree(/.*)$"); + + @Override + protected String readPath(HttpServletRequest request) { + Matcher matcher = REQUEST_PATTERN.matcher(request.getPathInfo()); + + if (matcher.matches()) { + return matcher.group(2); + } + + throw new IllegalStateException("handler bound at the wrong path"); + } + + @Override + protected RemoteRevision readRevision(HttpServletRequest request, RemoteSession session) { + Matcher matcher = REQUEST_PATTERN.matcher(request.getPathInfo()); + + if (matcher.matches()) { + return session.readRevision(matcher.group(1)); + } + + throw new IllegalStateException("handler bound at the wrong path"); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadTreeHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadTreeHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadTreeHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/HeadTreeHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,68 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; +import org.apache.jackrabbit.oak.remote.RemoteTree; +import org.apache.jackrabbit.oak.remote.RemoteTreeFilters; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static java.util.Collections.singletonMap; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendGone; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendInternalServerError; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendNotFound; + +abstract class HeadTreeHandler implements Handler { + + protected abstract String readPath(HttpServletRequest request); + + protected abstract RemoteRevision readRevision(HttpServletRequest request, RemoteSession session); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + RemoteSession session = (RemoteSession) request.getAttribute("session"); + + if (session == null) { + sendInternalServerError(response, "session not found"); + return; + } + + RemoteRevision revision = readRevision(request, session); + + if (revision == null) { + sendGone(response, "revision not found"); + return; + } + + RemoteTree tree = session.readTree(revision, readPath(request), new RemoteTreeFilters()); + + if (tree == null) { + sendNotFound(response, singletonMap("Oak-Revision", revision.asString()), "tree not found"); + return; + } + + response.setStatus(HttpServletResponse.SC_OK); + response.setHeader("Oak-Revision", revision.asString()); + response.setContentType("application/json"); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/NotFoundHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/NotFoundHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/NotFoundHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/NotFoundHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,33 @@ +/* + * 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.remote.http.handler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendNotFound; + +class NotFoundHandler implements Handler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + sendNotFound(response, "requested path not found"); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchLastRevisionHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchLastRevisionHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchLastRevisionHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchLastRevisionHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,32 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.http.HttpServletRequest; + +class PatchLastRevisionHandler extends PatchRevisionHandler { + + @Override + protected RemoteRevision readRevision(HttpServletRequest request, RemoteSession session) { + return session.readLastRevision(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchRevisionHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchRevisionHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchRevisionHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchRevisionHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,445 @@ +/* + * 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.remote.http.handler; + +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.jackrabbit.oak.remote.RemoteCommitException; +import org.apache.jackrabbit.oak.remote.RemoteOperation; +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; +import org.apache.jackrabbit.oak.remote.RemoteValue; +import org.apache.jackrabbit.oak.remote.RemoteValue.Supplier; +import org.apache.jackrabbit.util.Base64; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendBadRequest; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendGone; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendInternalServerError; + +abstract class PatchRevisionHandler implements Handler { + + private static final Logger logger = LoggerFactory.getLogger(PatchRevisionHandler.class); + + protected abstract RemoteRevision readRevision(HttpServletRequest request, RemoteSession session); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + RemoteSession session = (RemoteSession) request.getAttribute("session"); + + if (session == null) { + sendInternalServerError(response, "session not found"); + return; + } + + RemoteRevision base = readRevision(request, session); + + if (base == null) { + sendGone(response, "revision not found"); + return; + } + + RemoteOperation operation; + + try { + operation = parseOperations(session, new ObjectMapper().readTree(request.getInputStream())); + } catch (Exception e) { + operation = null; + } + + if (operation == null) { + sendBadRequest(response, "unable to parse the list of operations"); + return; + } + + RemoteRevision revision; + + try { + revision = session.commit(base, operation); + } catch (RemoteCommitException e) { + logger.warn("unable to perform the commit", e); + sendBadRequest(response, "commit failed"); + return; + } + + response.setStatus(HttpServletResponse.SC_CREATED); + response.setContentType("application/json"); + + ServletOutputStream stream = response.getOutputStream(); + + JsonGenerator generator = new JsonFactory().createJsonGenerator(stream, JsonEncoding.UTF8); + renderResponse(generator, revision); + generator.flush(); + + stream.close(); + } + + private void renderResponse(JsonGenerator generator, RemoteRevision revision) throws IOException { + generator.writeStartObject(); + generator.writeStringField("revision", revision.asString()); + generator.writeEndObject(); + } + + private RemoteOperation parseOperations(RemoteSession session, JsonNode json) { + List<RemoteOperation> operations = new ArrayList<RemoteOperation>(); + + for (JsonNode child : json) { + operations.add(parseOperation(session, child)); + } + + return session.createAggregateOperation(operations); + } + + private RemoteOperation parseOperation(RemoteSession session, JsonNode json) { + String type = parseStringField(json, "op"); + + if (type.equals("add")) { + return parseAddOperation(session, json); + } + + if (type.equals("remove")) { + return parseRemoveOperation(session, json); + } + + if (type.equals("set")) { + return parseSetOperation(session, json); + } + + if (type.equals("unset")) { + return parseUnsetOperation(session, json); + } + + if (type.equals("copy")) { + return parseCopyOperation(session, json); + } + + if (type.equals("move")) { + return parseMoveOperation(session, json); + } + + throw new IllegalArgumentException("invalid operation type"); + } + + private RemoteOperation parseMoveOperation(RemoteSession session, JsonNode node) { + return session.createMoveOperation(parseStringField(node, "from"), parseStringField(node, "to")); + } + + private RemoteOperation parseCopyOperation(RemoteSession session, JsonNode node) { + return session.createCopyOperation(parseStringField(node, "from"), parseStringField(node, "to")); + } + + private RemoteOperation parseUnsetOperation(RemoteSession session, JsonNode node) { + return session.createUnsetOperation(parseStringField(node, "path"), parseStringField(node, "name")); + } + + private RemoteOperation parseSetOperation(RemoteSession session, JsonNode node) { + return session.createSetOperation(parseStringField(node, "path"), parseStringField(node, "name"), parseValue(node)); + } + + private RemoteOperation parseRemoveOperation(RemoteSession session, JsonNode node) { + return session.createRemoveOperation(parseStringField(node, "path")); + } + + private RemoteOperation parseAddOperation(RemoteSession session, JsonNode json) { + return session.createAddOperation(parseStringField(json, "path"), parsePropertiesField(json, "properties")); + } + + private Map<String, RemoteValue> parsePropertiesField(JsonNode node, String name) { + return parseProperties(node.get(name)); + } + + private Map<String, RemoteValue> parseProperties(JsonNode node) { + Map<String, RemoteValue> values = new HashMap<String, RemoteValue>(); + + Iterator<Map.Entry<String, JsonNode>> iterator = node.fields(); + + while (iterator.hasNext()) { + Map.Entry<String, JsonNode> entry = iterator.next(); + values.put(entry.getKey(), parseValue(entry.getValue())); + } + + return values; + } + + private RemoteValue parseValue(JsonNode node) { + String type = parseStringField(node, "type"); + + if (type.equals("string")) { + return RemoteValue.toText(parseStringField(node, "value")); + } + + if (type.equals("strings")) { + return RemoteValue.toMultiText(parseStringArrayField(node, "value")); + } + + if (type.equals("binary")) { + return RemoteValue.toBinary(parseBinaryField(node, "value")); + } + + if (type.equals("binaries")) { + return RemoteValue.toMultiBinary(parseBinaryArrayField(node, "value")); + } + + if (type.equals("binaryId")) { + return RemoteValue.toBinaryId(parseStringField(node, "value")); + } + + if (type.equals("binaryIds")) { + return RemoteValue.toMultiBinaryId(parseStringArrayField(node, "value")); + } + + if (type.equals("long")) { + return RemoteValue.toLong(parseLongField(node, "value")); + } + + if (type.equals("longs")) { + return RemoteValue.toMultiLong(parseLongArrayField(node, "value")); + } + + if (type.equals("double")) { + return RemoteValue.toDouble(parseDoubleField(node, "value")); + } + + if (type.equals("doubles")) { + return RemoteValue.toMultiDouble(parseDoubleArrayField(node, "value")); + } + + if (type.equals("date")) { + return RemoteValue.toDate(parseLongField(node, "value")); + } + + if (type.equals("dates")) { + return RemoteValue.toMultiDate(parseLongArrayField(node, "value")); + } + + if (type.equals("boolean")) { + return RemoteValue.toBoolean(parseBooleanField(node, "value")); + } + + if (type.equals("booleans")) { + return RemoteValue.toMultiBoolean(parseBooleanArrayField(node, "value")); + } + + if (type.equals("name")) { + return RemoteValue.toName(parseStringField(node, "value")); + } + + if (type.equals("names")) { + return RemoteValue.toMultiName(parseStringArrayField(node, "value")); + } + + if (type.equals("path")) { + return RemoteValue.toPath(parseStringField(node, "value")); + } + + if (type.equals("paths")) { + return RemoteValue.toMultiPath(parseStringArrayField(node, "value")); + } + + if (type.equals("reference")) { + return RemoteValue.toReference(parseStringField(node, "value")); + } + + if (type.equals("references")) { + return RemoteValue.toMultiReference(parseStringArrayField(node, "value")); + } + + if (type.equals("weakReference")) { + return RemoteValue.toWeakReference(parseStringField(node, "value")); + } + + if (type.equals("weakReferences")) { + return RemoteValue.toMultiWeakReference(parseStringArrayField(node, "value")); + } + + if (type.equals("uri")) { + return RemoteValue.toUri(parseStringField(node, "value")); + } + + if (type.equals("uris")) { + return RemoteValue.toMultiUri(parseStringArrayField(node, "value")); + } + + if (type.equals("decimal")) { + return RemoteValue.toDecimal(parseDecimalField(node, "value")); + } + + if (type.equals("decimals")) { + return RemoteValue.toMultiDecimal(parseDecimalArrayField(node, "value")); + } + + throw new IllegalArgumentException("invalid value type"); + } + + private BigDecimal parseDecimalField(JsonNode node, String field) { + return parseDecimal(node.get(field)); + } + + private BigDecimal parseDecimal(JsonNode node) { + return new BigDecimal(node.asText()); + } + + private Iterable<BigDecimal> parseDecimalArrayField(JsonNode node, String field) { + return parseDecimalArray(node.get(field)); + } + + private Iterable<BigDecimal> parseDecimalArray(JsonNode node) { + List<BigDecimal> result = new ArrayList<BigDecimal>(); + + for (JsonNode element : node) { + result.add(parseDecimal(element)); + } + + return result; + } + + private boolean parseBooleanField(JsonNode node, String field) { + return parseBoolean(node.get(field)); + } + + private boolean parseBoolean(JsonNode node) { + return node.asBoolean(); + } + + private Iterable<Boolean> parseBooleanArrayField(JsonNode node, String field) { + return parseBooleanArray(node.get(field)); + } + + private Iterable<Boolean> parseBooleanArray(JsonNode node) { + List<Boolean> result = new ArrayList<Boolean>(); + + for (JsonNode element : node) { + result.add(parseBoolean(element)); + } + + return result; + } + + private double parseDoubleField(JsonNode node, String field) { + return parseDouble(node.get(field)); + } + + private double parseDouble(JsonNode node) { + return node.asDouble(); + } + + private Iterable<Double> parseDoubleArrayField(JsonNode node, String field) { + return parseDoubleArray(node.get(field)); + } + + private Iterable<Double> parseDoubleArray(JsonNode node) { + List<Double> result = new ArrayList<Double>(); + + for (JsonNode element : node) { + result.add(parseDouble(element)); + } + + return result; + } + + private long parseLongField(JsonNode node, String field) { + return parseLong(node.get(field)); + } + + private long parseLong(JsonNode node) { + return node.asLong(); + } + + private Iterable<Long> parseLongArrayField(JsonNode node, String field) { + return parseLongArray(node.get(field)); + } + + private Iterable<Long> parseLongArray(JsonNode node) { + List<Long> result = new ArrayList<Long>(); + + for (JsonNode element : node) { + result.add(parseLong(element)); + } + + return result; + } + + private Supplier<InputStream> parseBinaryField(JsonNode node, String field) { + return parseBinary(node.get(field)); + } + + private Supplier<InputStream> parseBinary(final JsonNode node) { + return new Supplier<InputStream>() { + + @Override + public InputStream get() { + return new ByteArrayInputStream(Base64.decode(node.asText()).getBytes()); + } + + }; + } + + private Iterable<Supplier<InputStream>> parseBinaryArrayField(JsonNode node, String field) { + return parseBinaryArray(node.get(field)); + } + + private Iterable<Supplier<InputStream>> parseBinaryArray(JsonNode node) { + List<Supplier<InputStream>> result = new ArrayList<Supplier<InputStream>>(); + + for (JsonNode element : node) { + result.add(parseBinary(element)); + } + + return result; + } + + private Iterable<String> parseStringArrayField(JsonNode node, String field) { + return parseStringArray(node.get(field)); + } + + private String parseStringField(JsonNode node, String field) { + return parseString(node.get(field)); + } + + private Iterable<String> parseStringArray(JsonNode node) { + List<String> result = new ArrayList<String>(); + + for (JsonNode element : node) { + result.add(parseString(element)); + } + + return result; + } + + private String parseString(JsonNode node) { + return node.asText(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchSpecificRevisionHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchSpecificRevisionHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchSpecificRevisionHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PatchSpecificRevisionHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,42 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class PatchSpecificRevisionHandler extends PatchRevisionHandler { + + private static final Pattern REQUEST_PATTERN = Pattern.compile("^/revisions/([^/]+)/tree$"); + + @Override + protected RemoteRevision readRevision(HttpServletRequest request, RemoteSession session) { + Matcher matcher = REQUEST_PATTERN.matcher(request.getPathInfo()); + + if (matcher.matches()) { + return session.readRevision(matcher.group(1)); + } + + throw new IllegalStateException("handler bound at the wrong path"); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PostBinaryHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PostBinaryHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PostBinaryHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/PostBinaryHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,60 @@ +/* + * 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.remote.http.handler; + +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.jackrabbit.oak.remote.RemoteBinaryId; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendInternalServerError; + +class PostBinaryHandler implements Handler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + RemoteSession session = (RemoteSession) request.getAttribute("session"); + + if (session == null) { + sendInternalServerError(response, "session not found"); + return; + } + + RemoteBinaryId binaryId = session.writeBinary(request.getInputStream()); + + response.setStatus(HttpServletResponse.SC_CREATED); + response.setContentType("application/json"); + + ServletOutputStream stream = response.getOutputStream(); + + JsonGenerator generator = new JsonFactory().createJsonGenerator(stream, JsonEncoding.UTF8); + generator.writeStartObject(); + generator.writeStringField("binaryId", binaryId.asString()); + generator.writeEndObject(); + generator.flush(); + + stream.close(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteValues.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteValues.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteValues.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/RemoteValues.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,245 @@ +/* + * 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.remote.http.handler; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.io.BaseEncoding; +import com.google.common.io.ByteStreams; +import org.apache.jackrabbit.oak.remote.RemoteValue; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; + +class RemoteValues { + + private RemoteValues() { + // Prevent instantiation + } + + public static void renderJsonOrNull(JsonGenerator generator, RemoteValue value) throws IOException { + if (value == null) { + generator.writeNull(); + } else { + renderJson(generator, value); + } + } + + public static void renderJson(JsonGenerator generator, RemoteValue value) throws IOException { + if (value.isBinary()) { + renderValue(generator, "binary", value.asBinary(), getBinaryWriter()); + } + + if (value.isMultiBinary()) { + renderMultiValue(generator, "binaries", value.asMultiBinary(), getBinaryWriter()); + } + + if (value.isBinaryId()) { + renderValue(generator, "binaryId", value.asBinaryId(), getStringWriter()); + } + + if (value.isMultiBinaryId()) { + renderMultiValue(generator, "binaryIds", value.asMultiBinaryId(), getStringWriter()); + } + + if (value.isBoolean()) { + renderValue(generator, "boolean", value.asBoolean(), getBooleanWriter()); + } + + if (value.isMultiBoolean()) { + renderMultiValue(generator, "booleans", value.asMultiBoolean(), getBooleanWriter()); + } + + if (value.isDate()) { + renderValue(generator, "date", value.asDate(), getLongWriter()); + } + + if (value.isMultiDate()) { + renderMultiValue(generator, "dates", value.asMultiDate(), getLongWriter()); + } + + if (value.isDecimal()) { + renderValue(generator, "decimal", value.asDecimal(), getDecimalWriter()); + } + + if (value.isMultiDecimal()) { + renderMultiValue(generator, "decimals", value.asMultiDecimal(), getDecimalWriter()); + } + + if (value.isDouble()) { + renderValue(generator, "double", value.asDouble(), getDoubleWriter()); + } + + if (value.isMultiDouble()) { + renderMultiValue(generator, "doubles", value.asMultiDouble(), getDoubleWriter()); + } + + if (value.isLong()) { + renderValue(generator, "long", value.asLong(), getLongWriter()); + } + + if (value.isMultiLong()) { + renderMultiValue(generator, "longs", value.asMultiLong(), getLongWriter()); + } + + if (value.isName()) { + renderValue(generator, "name", value.asName(), getStringWriter()); + } + + if (value.isMultiName()) { + renderMultiValue(generator, "names", value.asMultiName(), getStringWriter()); + } + + if (value.isPath()) { + renderValue(generator, "path", value.asPath(), getStringWriter()); + } + + if (value.isMultiPath()) { + renderMultiValue(generator, "paths", value.asMultiPath(), getStringWriter()); + } + + if (value.isReference()) { + renderValue(generator, "reference", value.asReference(), getStringWriter()); + } + + if (value.isMultiReference()) { + renderMultiValue(generator, "references", value.asMultiReference(), getStringWriter()); + } + + if (value.isText()) { + renderValue(generator, "string", value.asText(), getStringWriter()); + } + + if (value.isMultiText()) { + renderMultiValue(generator, "strings", value.asMultiText(), getStringWriter()); + } + + if (value.isUri()) { + renderValue(generator, "uri", value.asUri(), getStringWriter()); + } + + if (value.isMultiUri()) { + renderMultiValue(generator, "uris", value.asMultiUri(), getStringWriter()); + } + + if (value.isWeakReference()) { + renderValue(generator, "weakReference", value.asWeakReference(), getStringWriter()); + } + + if (value.isMultiWeakReference()) { + renderMultiValue(generator, "weakReferences", value.asMultiWeakReference(), getStringWriter()); + } + } + + private static GeneratorWriter<RemoteValue.Supplier<InputStream>> getBinaryWriter() { + return new GeneratorWriter<RemoteValue.Supplier<InputStream>>() { + + @Override + public void write(JsonGenerator generator, RemoteValue.Supplier<InputStream> value) throws IOException { + generator.writeString(BaseEncoding.base64().encode(ByteStreams.toByteArray(value.get()))); + } + + }; + } + + private static GeneratorWriter<String> getStringWriter() { + return new GeneratorWriter<String>() { + + @Override + public void write(JsonGenerator generator, String value) throws IOException { + generator.writeString(value); + } + + }; + } + + private static GeneratorWriter<Boolean> getBooleanWriter() { + return new GeneratorWriter<Boolean>() { + + @Override + public void write(JsonGenerator generator, Boolean value) throws IOException { + generator.writeBoolean(value); + } + + }; + } + + private static GeneratorWriter<Long> getLongWriter() { + return new GeneratorWriter<Long>() { + + @Override + public void write(JsonGenerator generator, Long value) throws IOException { + generator.writeNumber(value); + } + + }; + } + + private static GeneratorWriter<BigDecimal> getDecimalWriter() { + return new GeneratorWriter<BigDecimal>() { + + @Override + public void write(JsonGenerator generator, BigDecimal value) throws IOException { + generator.writeString(value.toString()); + } + + }; + } + + private static GeneratorWriter<Double> getDoubleWriter() { + return new GeneratorWriter<Double>() { + + @Override + public void write(JsonGenerator generator, Double value) throws IOException { + generator.writeNumber(value); + } + + }; + } + + private static <T> void renderValue(JsonGenerator generator, String type, T value, GeneratorWriter<T> writer) throws IOException { + generator.writeStartObject(); + generator.writeStringField("type", type); + generator.writeFieldName("value"); + + writer.write(generator, value); + + generator.writeEndObject(); + } + + private static <T> void renderMultiValue(JsonGenerator generator, String type, Iterable<T> values, GeneratorWriter<T> writer) throws IOException { + generator.writeStartObject(); + generator.writeStringField("type", type); + generator.writeArrayFieldStart("value"); + + for (T value : values) { + writer.write(generator, value); + } + + generator.writeEndArray(); + generator.writeEndObject(); + } + + + private interface GeneratorWriter<T> { + + void write(JsonGenerator generator, T value) throws IOException; + + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/ResponseUtils.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/ResponseUtils.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/ResponseUtils.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/ResponseUtils.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,87 @@ +/* + * 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.remote.http.handler; + +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +class ResponseUtils { + + private ResponseUtils() { + } + + private static void send(HttpServletResponse response, int code, String message) throws IOException { + send(response, code, new HashMap<String, String>(), message); + } + + private static void send(HttpServletResponse response, int code, Map<String, String> headers, String message) throws IOException { + response.setStatus(code); + response.setContentType("application/json"); + + for (Map.Entry<String, String> entry : headers.entrySet()) { + response.setHeader(entry.getKey(), entry.getValue()); + } + + ServletOutputStream stream = response.getOutputStream(); + + JsonGenerator generator = new JsonFactory().createJsonGenerator(stream, JsonEncoding.UTF8); + generator.writeStartObject(); + generator.writeStringField("error", message); + generator.writeEndObject(); + generator.flush(); + + stream.close(); + } + + public static void sendBadRequest(HttpServletResponse response, String message) throws IOException { + send(response, HttpServletResponse.SC_BAD_REQUEST, message); + } + + public static void sendInternalServerError(HttpServletResponse response, String message) throws IOException { + send(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message); + } + + public static void sendGone(HttpServletResponse response, String message) throws IOException { + send(response, HttpServletResponse.SC_GONE, message); + } + + public static void sendNotFound(HttpServletResponse response, String message) throws IOException { + send(response, HttpServletResponse.SC_NOT_FOUND, message); + } + + public static void sendNotFound(HttpServletResponse response, Map<String, String> headers, String message) throws IOException { + send(response, HttpServletResponse.SC_NOT_FOUND, headers, message); + } + + public static void sendUnauthorized(HttpServletResponse response, String message) throws IOException { + send(response, HttpServletResponse.SC_UNAUTHORIZED, message); + } + + public static void sendUnauthorized(HttpServletResponse response, Map<String, String> headers, String message) throws IOException { + send(response, HttpServletResponse.SC_UNAUTHORIZED, headers, message); + } + + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchLastRevisionHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchLastRevisionHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchLastRevisionHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchLastRevisionHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,32 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.http.HttpServletRequest; + +class SearchLastRevisionHandler extends SearchRevisionHandler { + + @Override + protected RemoteRevision readRevision(HttpServletRequest request, RemoteSession session) { + return session.readLastRevision(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchRevisionHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchRevisionHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchRevisionHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchRevisionHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,209 @@ +/* + * 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.remote.http.handler; + +import com.fasterxml.jackson.core.JsonEncoding; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.jackrabbit.oak.remote.RemoteQueryParseException; +import org.apache.jackrabbit.oak.remote.RemoteResult; +import org.apache.jackrabbit.oak.remote.RemoteResults; +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static org.apache.jackrabbit.oak.remote.http.handler.RemoteValues.renderJson; +import static org.apache.jackrabbit.oak.remote.http.handler.RemoteValues.renderJsonOrNull; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendBadRequest; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendGone; +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendInternalServerError; + +abstract class SearchRevisionHandler implements Handler { + + protected abstract RemoteRevision readRevision(HttpServletRequest request, RemoteSession session); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + RemoteSession session = (RemoteSession) request.getAttribute("session"); + + if (session == null) { + sendInternalServerError(response, "session not found"); + return; + } + + RemoteRevision revision = readRevision(request, session); + + if (revision == null) { + sendGone(response, "unable to read the revision"); + return; + } + + String query = readQuery(request); + + if (query == null) { + sendBadRequest(response, "query not specified"); + return; + } + + String language = readLanguage(request); + + if (language == null) { + sendBadRequest(response, "language not specified"); + return; + } + + Long offset = readOffset(request); + + if (offset == null) { + sendBadRequest(response, "offset not specified"); + return; + } + + Long limit = readLimit(request); + + if (limit == null) { + sendBadRequest(response, "limit not specified"); + return; + } + + RemoteResults results; + + try { + results = session.search(revision, query, language, offset, limit); + } catch (RemoteQueryParseException e) { + sendBadRequest(response, "malformed query"); + return; + } + + response.setStatus(HttpServletResponse.SC_OK); + response.setHeader("Oak-Revision", revision.asString()); + response.setContentType("application/json"); + + ServletOutputStream stream = response.getOutputStream(); + + JsonGenerator generator = new JsonFactory().createJsonGenerator(stream, JsonEncoding.UTF8); + renderResponse(generator, results); + generator.flush(); + + stream.close(); + } + + private String readQuery(HttpServletRequest request) { + return readStringParameter(request, "query"); + } + + private String readLanguage(HttpServletRequest request) { + return readStringParameter(request, "language"); + } + + private String readStringParameter(HttpServletRequest request, String name) { + return request.getParameter(name); + } + + private Long readOffset(HttpServletRequest request) { + return readLongParameter(request, "offset"); + } + + private Long readLimit(HttpServletRequest request) { + return readLongParameter(request, "limit"); + } + + private Long readLongParameter(HttpServletRequest request, String name) { + String value = readStringParameter(request, name); + + if (value == null) { + return null; + } + + try { + return Long.parseLong(value, 10); + } catch (NumberFormatException e) { + return null; + } + } + + private void renderResponse(JsonGenerator generator, RemoteResults results) throws IOException { + generator.writeStartObject(); + generator.writeFieldName("total"); + generator.writeNumber(results.getTotal()); + generator.writeFieldName("columns"); + renderStrings(generator, results.getColumns()); + generator.writeFieldName("selectors"); + renderStrings(generator, results.getSelectors()); + generator.writeFieldName("results"); + renderResults(generator, results); + generator.writeEndObject(); + } + + private void renderStrings(JsonGenerator generator, Iterable<String> elements) throws IOException { + generator.writeStartArray(); + + for (String element : elements) { + generator.writeString(element); + } + + generator.writeEndArray(); + } + + private void renderResults(JsonGenerator generator, RemoteResults results) throws IOException { + generator.writeStartArray(); + + for (RemoteResult result : results) { + renderResult(generator, results, result); + } + + generator.writeEndArray(); + } + + private void renderResult(JsonGenerator generator, RemoteResults results, RemoteResult result) throws IOException { + generator.writeStartObject(); + generator.writeFieldName("columns"); + renderColumns(generator, results, result); + generator.writeFieldName("selectors"); + renderSelectors(generator, results, result); + generator.writeEndObject(); + } + + private void renderColumns(JsonGenerator generator, RemoteResults results, RemoteResult result) throws IOException { + generator.writeStartObject(); + + for (String name : results.getColumns()) { + generator.writeFieldName(name); + renderJsonOrNull(generator, result.getColumnValue(name)); + } + + generator.writeEndObject(); + } + + private void renderSelectors(JsonGenerator generator, RemoteResults results, RemoteResult result) throws IOException { + generator.writeStartObject(); + + for (String name : results.getSelectors()) { + generator.writeFieldName(name); + generator.writeString(result.getSelectorPath(name)); + } + + generator.writeEndObject(); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchSpecificRevisionHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchSpecificRevisionHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchSpecificRevisionHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/SearchSpecificRevisionHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,42 @@ +/* + * 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.remote.http.handler; + +import org.apache.jackrabbit.oak.remote.RemoteRevision; +import org.apache.jackrabbit.oak.remote.RemoteSession; + +import javax.servlet.http.HttpServletRequest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +class SearchSpecificRevisionHandler extends SearchRevisionHandler { + + private static final Pattern REQUEST_PATTERN = Pattern.compile("^/revisions/([^/]+)/tree$"); + + @Override + protected RemoteRevision readRevision(HttpServletRequest request, RemoteSession session) { + Matcher matcher = REQUEST_PATTERN.matcher(request.getPathInfo()); + + if (matcher.matches()) { + return session.readRevision(matcher.group(1)); + } + + throw new IllegalStateException("handler bound at the wrong path"); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/UnauthorizedHandler.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/UnauthorizedHandler.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/UnauthorizedHandler.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/handler/UnauthorizedHandler.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,34 @@ +/* + * 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.remote.http.handler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Collections; + +import static org.apache.jackrabbit.oak.remote.http.handler.ResponseUtils.sendUnauthorized; + +class UnauthorizedHandler implements Handler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response) throws IOException { + sendUnauthorized(response, Collections.singletonMap("WWW-Authenticate", "Basic realm=\"Oak\""), "unable to authenticate"); + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcher.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcher.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcher.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/AllMatcher.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,41 @@ +/* + * 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.remote.http.matcher; + +import javax.servlet.http.HttpServletRequest; + +class AllMatcher implements Matcher { + + private final Matcher[] matchers; + + public AllMatcher(Matcher... matchers) { + this.matchers = matchers; + } + + @Override + public boolean match(HttpServletRequest request) { + for (Matcher matcher : matchers) { + if (!matcher.match(request)) { + return false; + } + } + + return true; + } + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matcher.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matcher.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matcher.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matcher.java Thu Jun 11 12:09:15 2015 @@ -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.remote.http.matcher; + +import javax.servlet.http.HttpServletRequest; + +/** + * A predicate over an HTTP request. This predicate can be used to check if some + * preconditions on the request are met. + */ +public interface Matcher { + + /** + * Check if the preconditions on the given request are met. + * + * @param request Request to check. + * @return {@code true} if the preconditions are met, {@code false} + * otherwise. + */ + boolean match(HttpServletRequest request); + +} Added: jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matchers.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matchers.java?rev=1684861&view=auto ============================================================================== --- jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matchers.java (added) +++ jackrabbit/oak/trunk/oak-remote/src/main/java/org/apache/jackrabbit/oak/remote/http/matcher/Matchers.java Thu Jun 11 12:09:15 2015 @@ -0,0 +1,100 @@ +/* + * 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.remote.http.matcher; + +import java.util.regex.Pattern; + +/** + * Collection of matchers for HTTP requests. + */ +public class Matchers { + + private Matchers() { + } + + /** + * Create a matcher that will be satisfied when given requests have a method + * matching the one provided as a parameter. + * + * @param method Method that requests must have for the matcher to be + * satisfied. + * @return An instance of {@code Matcher}. + */ + public static Matcher matchesMethod(String method) { + if (method == null) { + throw new IllegalArgumentException("method not provided"); + } + + return new MethodMatcher(method); + } + + /** + * Create a matcher that will be satisfied when given requests have a patch + * matching the pattern provided as a parameter. + * + * @param pattern The pattern to use when checking the requests given to the + * matcher. + * @return An instance of {@code Matcher}. + */ + public static Matcher matchesPath(String pattern) { + if (pattern == null) { + throw new IllegalArgumentException("pattern not provided"); + } + + return new PathMatcher(Pattern.compile(pattern)); + } + + /** + * Create a matcher that will be satisfied when the given requests satisfies + * every matcher provided as parameters. Calling this method is equivalent + * as checking every provided matcher individually and chaining each result + * as a short-circuit and. + * + * @param matchers The matchers that have to be satisfied for the returned + * matcher to be satisfied. + * @return An instance of {@code Matcher}. + */ + public static Matcher matchesAll(Matcher... matchers) { + if (matchers == null) { + throw new IllegalArgumentException("matchers not provided"); + } + + for (Matcher matcher : matchers) { + if (matcher == null) { + throw new IllegalArgumentException("invalid matcher"); + } + } + + return new AllMatcher(matchers); + } + + /** + * Create a matcher that will be satisifed when the given requests match the + * provided method and path. + * + * @param method The method that requests must have for the matcher to be + * satisfied. + * @param path The pattern to use when checking the requests given to the + * matcher. + * @return An instance of {@code Matcher}. + */ + public static Matcher matchesRequest(String method, String path) { + return matchesAll(matchesMethod(method), matchesPath(path)); + } + +}
