Java tutorial
/* * Copyright 2014 Samppa Saarela * * Licensed 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.javersion.json.web; import static org.javersion.core.Version.DEFAULT_BRANCH; import static org.javersion.path.PropertyPath.ROOT; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; import static org.springframework.web.bind.annotation.RequestMethod.GET; import static org.springframework.web.bind.annotation.RequestMethod.POST; import static org.springframework.web.bind.annotation.RequestMethod.PUT; import java.net.URI; import java.net.URISyntaxException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.inject.Inject; import org.javersion.core.Merge; import org.javersion.core.Persistent; import org.javersion.core.Revision; import org.javersion.core.Version; import org.javersion.json.JsonSerializer; import org.javersion.path.PropertyPath; import org.javersion.store.jdbc.ObjectVersionStoreJdbc; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.google.common.collect.ImmutableSet; /** * GET: /objects/{objectId} - get default branch * PUT: /objects/{objectId} - update default branch * * GET: /objects/{objectId}/{branch|revision} * PUT: /objects/{objectId}/{branch|revision} * * GET: /versions/{objectId} * PUT: /versions/{objectId} - rewrite */ @RestController public class JsonStoreController { private final JsonStoreConfig config; @Inject ObjectVersionStoreJdbc<Void> objectVersionStore; private final TypeMappings metaTypeMappings = TypeMappings.builder().withMapping(new VersionPropertyMapping()) .build(); private final ObjectSerializer<VersionMetadata> metaSerializer = new ObjectSerializer<>(VersionMetadata.class, metaTypeMappings); private final JsonSerializer jsonSerializer = new JsonSerializer(new JsonSerializer.Config(false, false, ""), metaSerializer.schemaRoot); public JsonStoreController() { this(new JsonStoreConfig()); } public JsonStoreController(JsonStoreConfig config) { this.config = config; } @RequestMapping(value = "/objects", method = POST) public ResponseEntity<String> postObject(@RequestBody String json) { String objectId = UUID.randomUUID().toString(); return putObject(objectId, json, DEFAULT_BRANCH, true); } @RequestMapping(value = "/objects/{objectId}", method = PUT) public ResponseEntity<String> putObject(@PathVariable("objectId") String objectId, @RequestBody String json) { return putObject(objectId, json, DEFAULT_BRANCH, false); } @RequestMapping(value = "/objects/{objectId}/branches/{branch}", method = PUT) public ResponseEntity<String> putObjectOnBranch(@PathVariable("objectId") String objectId, @PathVariable("branch") String branch, @RequestBody String json) { return putObject(objectId, json, branch, false); } @RequestMapping(value = "/objects/{objectId}", method = GET) public ResponseEntity<String> getObject(@PathVariable("objectId") String objectId, @RequestParam(value = "merge", required = false) Set<String> merge) { return getBranch(objectId, DEFAULT_BRANCH, merge); } @RequestMapping(value = "/objects/{objectId}/branches/{branch}", method = GET) public ResponseEntity<String> getBranch(@PathVariable("objectId") String objectId, @PathVariable("branch") String branch, @RequestParam(value = "merge", required = false) Set<String> merge) { if (merge == null) { return getObject(objectId, null, branchesBuilder().add(branch).build(), false); } return getObject(objectId, null, branchesBuilder().add(branch).addAll(merge).build(), false); } @RequestMapping(value = "/objects/{objectId}/versions/{revision}", method = GET) public ResponseEntity<String> getVersion(@PathVariable("objectId") String objectId, @PathVariable("revision") Revision revision, @RequestParam(value = "merge", required = false) Set<String> merge) { if (merge == null) { return getObject(objectId, revision, branchesBuilder().build(), false); } return getObject(objectId, revision, branchesBuilder().addAll(merge).build(), false); } @RequestMapping(value = "/objects/{objectId}/versions", method = GET) public List<Version<PropertyPath, Object, Void>> getVersions(@PathVariable("objectId") String objectId) { ObjectVersionGraph<Void> versionGraph = objectVersionStore.load(objectId); if (versionGraph.isEmpty()) { throw new NotFoundException(); } return versionGraph.getVersions(); } private ResponseEntity<String> getObject(String objectId, Revision revision, Set<String> mergeBranches, boolean create) { ObjectVersionGraph<Void> versionGraph = objectVersionStore.load(objectId); if (versionGraph.isEmpty()) { throw new NotFoundException(); } return getResponse(objectId, versionGraph, revision, mergeBranches, create); } private ResponseEntity<String> putObject(String objectId, String json, String branch, boolean create) { JsonSerializer.JsonPaths paths = jsonSerializer.parse(json); if (!(paths.properties.get(ROOT) instanceof Persistent.Object)) { throw new IllegalArgumentException( "Expected root object to be an Object, got " + paths.properties.get(ROOT)); } VersionMetadata ref = metaSerializer.fromPropertyMap(paths.meta); ObjectVersionGraph<Void> versionGraph = objectVersionStore.load(objectId); ObjectVersionBuilder<Void> versionBuilder = new ObjectVersionBuilder<>(); if (ref != null && ref._revs != null) { versionBuilder.parents(ref._revs); } else if (!versionGraph.isEmpty()) { versionBuilder.parents(getParentsForUnreferencedUpdate(versionGraph, branch)); } versionBuilder.branch(branch); versionBuilder.changeset(paths.properties, versionGraph); ObjectVersion<Void> version = versionBuilder.build(); objectVersionStore.append(objectId, versionGraph.getVersionNode(version.revision)); objectVersionStore.commit(); return getObject(objectId, null, ImmutableSet.of(branch), create); } private Iterable<Revision> getParentsForUnreferencedUpdate(ObjectVersionGraph<Void> versionGraph, String branch) { for (JsonStoreConfig.UnreferencedUpdateStrategy strategy : config.unreferencedUpdateStrategy) { Iterable<Revision> parents = strategy.getParents(versionGraph, branch); if (parents != null) { return parents; } } throw new IllegalArgumentException("_revs missing, unreferenced update not allowed"); } private ResponseEntity<String> getResponse(String objectId, ObjectVersionGraph<Void> versionGraph, Revision revision, Set<String> mergeBranches, boolean create) { if (revision != null) { versionGraph = versionGraph.at(revision); } if (mergeBranches.isEmpty()) { // Specific version requested return getResponse(objectId, versionGraph.getVersionNode(revision), create); } else { // If revision is given, merge branches of that time return getResponse(objectId, versionGraph.mergeBranches(mergeBranches), create); } } private ResponseEntity<String> getResponse(String objectId, Merge<PropertyPath, Object, Void> merge, boolean create) { Map<PropertyPath, Object> properties = new HashMap<>(merge.getProperties()); VersionMetadata ref = new VersionMetadata(objectId, merge.getMergeHeads(), merge.conflicts); properties.putAll(metaSerializer.toPropertyMap(ref)); HttpHeaders headers = new HttpHeaders(); headers.set("Content-Type", "application/json;charset=UTF-8"); HttpStatus status; if (create) { status = CREATED; try { headers.setLocation(new URI("/objects/" + objectId)); } catch (URISyntaxException e) { throw new Error(e); } } else { status = OK; } return new ResponseEntity<String>(jsonSerializer.serialize(properties), headers, status); } private static ImmutableSet.Builder<String> branchesBuilder() { return ImmutableSet.<String>builder(); } }