Java tutorial
/* * This file is provided 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 com.basho.riak.client.raw.pbc; import static com.basho.riak.client.util.CharsetUtils.asBytes; import static com.basho.riak.client.util.CharsetUtils.getCharset; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import org.apache.http.impl.cookie.DateParseException; import org.apache.http.impl.cookie.DateUtils; import com.basho.riak.client.IRiakObject; import com.basho.riak.client.RiakLink; import com.basho.riak.client.bucket.BucketProperties; import com.basho.riak.client.builders.BucketPropertiesBuilder; import com.basho.riak.client.builders.RiakObjectBuilder; import com.basho.riak.client.cap.VClock; import com.basho.riak.client.convert.ConversionException; import com.basho.riak.client.query.LinkWalkStep.Accumulate; import com.basho.riak.client.query.MapReduceResult; import com.basho.riak.client.query.WalkResult; import com.basho.riak.client.query.indexes.BinIndex; import com.basho.riak.client.query.indexes.IntIndex; import com.basho.riak.client.raw.DeleteMeta; import com.basho.riak.client.raw.FetchMeta; import com.basho.riak.client.raw.RiakResponse; import com.basho.riak.client.raw.StoreMeta; import com.basho.riak.client.util.CharsetUtils; import com.basho.riak.client.util.UnmodifiableIterator; import com.basho.riak.pbc.FetchResponse; import com.basho.riak.pbc.MapReduceResponseSource; import com.basho.riak.pbc.RequestMeta; import com.basho.riak.pbc.RiakObject; import com.google.protobuf.ByteString; /** * * @author russell * */ public final class ConversionUtil { /** * the string to append to the content-type, before the charset name */ private static final String CHARSET = "; charset="; /** * All static methods, don't allow any instances to be created. */ private ConversionUtil() { } /** * @param fetch * @return */ static RiakResponse convert(com.basho.riak.pbc.RiakObject[] pbcObjects) { RiakResponse response = RiakResponse.empty(); if (pbcObjects != null && pbcObjects.length > 0) { IRiakObject[] converted = new IRiakObject[pbcObjects.length]; for (int i = 0; i < pbcObjects.length; i++) { converted[i] = convert(pbcObjects[i]); } response = new RiakResponse(pbcObjects[0].getVclock().toByteArray(), converted); } return response; } /** * Deal with a more detailed response (maybe from a conditional fetch) * * @param fetchResponse * @return a {@link RiakResponse} */ static RiakResponse convert(FetchResponse fetchResponse) { if (fetchResponse.isUnchanged()) { return RiakResponse.unmodified(); } RiakObject[] objects = fetchResponse.getObjects(); byte[] vclock = fetchResponse.getVClock(); // no objects + vclock == deleted vclock if ((objects == null || objects.length == 0) && vclock != null) { return new RiakResponse(vclock); } return convert(fetchResponse.getObjects()); } /** * @param o * @return */ static IRiakObject convert(com.basho.riak.pbc.RiakObject o) { RiakObjectBuilder builder = RiakObjectBuilder.newBuilder(o.getBucket(), o.getKey()); builder.withValue(nullSafeToBytes(o.getValue())); builder.withVClock(nullSafeToBytes(o.getVclock())); builder.withVtag(o.getVtag()); Date lastModified = o.getLastModified(); if (lastModified != null) { builder.withLastModified(lastModified.getTime()); } final Collection<RiakLink> links = new ArrayList<RiakLink>(); for (com.basho.riak.pbc.RiakLink link : o.getLinks()) { links.add(convert(link)); } builder.withLinks(links); @SuppressWarnings("rawtypes") final Collection<com.basho.riak.client.http.RiakIndex> indexes = o.getIndexes(); for (@SuppressWarnings("rawtypes") com.basho.riak.client.http.RiakIndex i : indexes) { if (i instanceof com.basho.riak.client.http.IntIndex) { builder.addIndex(i.getName(), (Integer) i.getValue()); } if (i instanceof com.basho.riak.client.http.BinIndex) { builder.addIndex(i.getName(), (String) i.getValue()); } } String ctype = o.getContentType(); String charset = o.getCharset(); if (CharsetUtils.hasCharset(ctype) || charset == null || "".equals(charset.trim())) { builder.withContentType(ctype); } else { builder.withContentType(ctype + CHARSET + charset); } final Map<String, String> userMetaData = new HashMap<String, String>(o.getUsermeta()); builder.withUsermeta(userMetaData); return builder.build(); } /** * @param link * @return */ private static RiakLink convert(com.basho.riak.pbc.RiakLink link) { return new RiakLink(nullSafeToStringUtf8(link.getBucket()), nullSafeToStringUtf8(link.getKey()), nullSafeToStringUtf8(link.getTag())); } /** * @param vclock * @return */ static byte[] nullSafeToBytes(ByteString value) { return value == null ? null : value.toByteArray(); } /** * @param value * @return */ static String nullSafeToStringUtf8(ByteString value) { return value == null ? null : value.toStringUtf8(); } static ByteString nullSafeToByteString(String value) { return value == null ? null : ByteString.copyFromUtf8(value); } /** * @param vclock * @return the vclock bytes, or null if vclock is null */ static byte[] nullSafeToBytes(VClock vclock) { return vclock == null ? null : vclock.getBytes(); } /** * Convert a {@link StoreMeta} to a pbc {@link RequestMeta} * * @param storeMeta * a {@link StoreMeta} for the store operation. * @return a {@link RequestMeta} populated from the storeMeta's values. */ static RequestMeta convert(StoreMeta storeMeta, IRiakObject riakObject) { RequestMeta requestMeta = new RequestMeta(); if (storeMeta.hasW()) { requestMeta.w(storeMeta.getW().getIntValue()); } if (storeMeta.hasDw()) { requestMeta.dw(storeMeta.getDw().getIntValue()); } if (storeMeta.hasReturnBody()) { requestMeta.returnBody(storeMeta.getReturnBody()); } if (storeMeta.hasReturnHead()) { requestMeta.returnHead(storeMeta.getReturnHead()); } String contentType = riakObject.getContentType(); if (contentType != null) { requestMeta.contentType(contentType); } if (storeMeta.hasPw()) { requestMeta.pw(storeMeta.getPw().getIntValue()); } if (storeMeta.hasIfNoneMatch()) { requestMeta.ifNoneMatch(storeMeta.getIfNoneMatch()); } if (storeMeta.hasIfNotModified()) { requestMeta.ifNotModified(storeMeta.getIfNotModified()); } return requestMeta; } /** * Convert a {@link IRiakObject} to a pbc * {@link com.basho.riak.pbc.RiakObject} * * @param riakObject * the RiakObject to convert * @return a {@link com.basho.riak.pbc.RiakObject} populated from riakObject */ static com.basho.riak.pbc.RiakObject convert(IRiakObject riakObject) { final VClock vc = riakObject.getVClock(); ByteString bucketName = nullSafeToByteString(riakObject.getBucket()); ByteString key = nullSafeToByteString(riakObject.getKey()); ByteString content = ByteString.copyFrom(riakObject.getValue()); ByteString vclock = null; if (vc != null) { vclock = nullSafeFromBytes(vc.getBytes()); } com.basho.riak.pbc.RiakObject result = new com.basho.riak.pbc.RiakObject(vclock, bucketName, key, content); for (com.basho.riak.client.RiakLink link : riakObject) { result.addLink(link.getTag(), link.getBucket(), link.getKey()); } for (Entry<String, String> metaDataItem : riakObject.userMetaEntries()) { result.addUsermetaItem(metaDataItem.getKey(), metaDataItem.getValue()); } // copy the indexes for (Map.Entry<IntIndex, Set<Integer>> i : riakObject.allIntIndexes().entrySet()) { String name = i.getKey().getFullname(); for (Integer v : i.getValue()) { result.addIndex(name, v); } } // copy the indexes for (Map.Entry<BinIndex, Set<String>> i : riakObject.allBinIndexes().entrySet()) { String name = i.getKey().getFullname(); for (String v : i.getValue()) { result.addIndex(name, v); } } String ctype = riakObject.getContentType(); if (CharsetUtils.hasCharset(ctype)) { result.setCharset(CharsetUtils.getDeclaredCharset(ctype)); ctype = ctype.split(";")[0]; } result.setContentType(ctype); return result; } /** * @param bytes * @return */ static ByteString nullSafeFromBytes(byte[] bytes) { return ByteString.copyFrom(bytes); } /** * @param bucketProperties * @return */ static com.basho.riak.pbc.BucketProperties convert(BucketProperties p) { return new com.basho.riak.pbc.BucketProperties().nValue(p.getNVal()).allowMult(p.getAllowSiblings()); } /** * @param properties * @return */ static BucketProperties convert(com.basho.riak.pbc.BucketProperties properties) { return new BucketPropertiesBuilder().allowSiblings(properties.getAllowMult()).nVal(properties.getNValue()) .build(); } /** * @param response * @return * @throws IOException */ static MapReduceResult convert(final MapReduceResponseSource response) throws IOException { return PBMapReduceResult.newResult(response); } /** * Converts an Http {@link Accumulate} value into a boolean * * @param accumulate * the {@link Accumulate} value * @param isFinalStep * is the {@link Accumulate} value for the final step * @return true if a m/r link phase should keep the result or false * otherwise */ static boolean linkAccumulateToLinkPhaseKeep(Accumulate accumulate, boolean isFinalStep) { // (in m/r terms we *always* want to keep the final step since its // output // is the input to the final map stage which we *do* keep) boolean keep = true; if (!isFinalStep) { switch (accumulate) { case YES: keep = true; break; case NO: case DEFAULT: keep = false; break; default: break; } } return keep; } /** * Take a link walked m/r result and make it into a WalkResult. * * This is a little bit nasty since the JSON is parsed to a Map. * * @param secondPhaseResult * the contents of which *must* be a json array of {step: int, v: * riakObjectMap} * @return a WalkResult of RiakObjects grouped by first-phase step * @throws IOException */ @SuppressWarnings({ "rawtypes" }) static WalkResult convert(MapReduceResult secondPhaseResult) throws IOException { final SortedMap<Integer, Collection<IRiakObject>> steps = new TreeMap<Integer, Collection<IRiakObject>>(); try { Collection<Map> results = secondPhaseResult.getResult(Map.class); for (Map o : results) { final int step = Integer.parseInt((String) o.get("step")); Collection<IRiakObject> stepAccumulator = steps.get(step); if (stepAccumulator == null) { stepAccumulator = new ArrayList<IRiakObject>(); steps.put(step, stepAccumulator); } final Map data = (Map) o.get("v"); stepAccumulator.add(mapToRiakObject(data)); } } catch (ConversionException e) { throw new IOException(e.getMessage()); } // create a result instance return new WalkResult() { public Iterator<Collection<IRiakObject>> iterator() { return new UnmodifiableIterator<Collection<IRiakObject>>(steps.values().iterator()); } }; } /** * Copy the data from the map into a RiakObject. * * @param data * a valid Map from JSON. * @return A RiakObject populated from the map. */ @SuppressWarnings({ "rawtypes", "unchecked" }) private static IRiakObject mapToRiakObject(Map data) { RiakObjectBuilder b = RiakObjectBuilder.newBuilder((String) data.get("bucket"), (String) data.get("key")); String vclock = (String) data.get("vclock"); b.withVClock(CharsetUtils.utf8StringToBytes(vclock)); final List values = (List) data.get("values"); // TODO figure out what to do about multiple values here, // I say take the first for now (that is what the link walk interface // does) if (values.size() != 0) { final Map value = (Map) values.get(0); final Map meta = (Map) value.get("metadata"); final String contentType = (String) meta.get("content-type"); b.withValue(asBytes((String) value.get("data"), getCharset(contentType))); b.withContentType(contentType); b.withVtag((String) meta.get("X-Riak-VTag")); try { Date lastModDate = DateUtils.parseDate((String) meta.get("X-Riak-Last-Modified")); b.withLastModified(lastModDate.getTime()); } catch (DateParseException e) { // NO-OP } List<List<String>> links = (List<List<String>>) meta.get("Links"); for (List<String> link : links) { b.addLink(link.get(0), link.get(1), link.get(2)); } Map<String, String> userMetaData = (Map<String, String>) meta.get("X-Riak-Meta"); b.withUsermeta(userMetaData); } return b.build(); } /** * Convert a {@link FetchMeta} to a {@link com.basho.riak.pbc.FetchMeta} * @param fm the {@link FetchMeta} to convert * @return the {@link com.basho.riak.pbc.FetchMeta} with the same values */ static com.basho.riak.pbc.FetchMeta convert(FetchMeta fm) { if (fm != null) { return new com.basho.riak.pbc.FetchMeta(fm.hasR() ? fm.getR().getIntValue() : null, fm.hasPr() ? fm.getPr().getIntValue() : null, fm.getNotFoundOK(), fm.getBasicQuorum(), fm.getHeadOnly(), fm.getReturnDeletedVClock(), fm.getIfModifiedVClock()); } else { return com.basho.riak.pbc.FetchMeta.empty(); } } /** * Convert a {@link DeleteMeta} to a {@link com.basho.riak.pbc.DeletehMeta} * @param dm the {@link FetchMeta} to convert * @return the {@link com.basho.riak.pbc.FetchMeta} with the same values */ static com.basho.riak.pbc.DeleteMeta convert(DeleteMeta dm) { if (dm != null) { return new com.basho.riak.pbc.DeleteMeta(dm.hasR() ? dm.getR().getIntValue() : null, dm.hasPr() ? dm.getPr().getIntValue() : null, dm.hasW() ? dm.getW().getIntValue() : null, dm.hasDw() ? dm.getDw().getIntValue() : null, dm.hasPw() ? dm.getPw().getIntValue() : null, dm.hasRw() ? dm.getRw().getIntValue() : null, nullSafeToBytes(dm.getVclock())); } else { return com.basho.riak.pbc.DeleteMeta.empty(); } } }