/*
Copyright (c) 2012 LinkedIn Corp.
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 com.linkedin.restli.examples.greetings.server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import com.linkedin.data.transform.DataProcessingException;
import com.linkedin.restli.common.HttpStatus;
import com.linkedin.restli.common.PatchRequest;
import com.linkedin.restli.examples.StringTestKeys;
import com.linkedin.restli.examples.greetings.api.Message;
import com.linkedin.restli.examples.greetings.api.Tone;
import com.linkedin.restli.server.BatchCreateRequest;
import com.linkedin.restli.server.BatchCreateResult;
import com.linkedin.restli.server.BatchDeleteRequest;
import com.linkedin.restli.server.BatchPatchRequest;
import com.linkedin.restli.server.BatchResult;
import com.linkedin.restli.server.BatchUpdateRequest;
import com.linkedin.restli.server.BatchUpdateResult;
import com.linkedin.restli.server.CreateResponse;
import com.linkedin.restli.server.PagingContext;
import com.linkedin.restli.server.RestLiServiceException;
import com.linkedin.restli.server.UpdateResponse;
import com.linkedin.restli.server.annotations.Finder;
import com.linkedin.restli.server.annotations.Optional;
import com.linkedin.restli.server.annotations.PagingContextParam;
import com.linkedin.restli.server.annotations.QueryParam;
import com.linkedin.restli.server.annotations.RestLiCollection;
import com.linkedin.restli.server.annotations.RestMethod;
import com.linkedin.restli.server.resources.CollectionResourceTemplate;
import com.linkedin.restli.server.util.PatchApplier;
/**
* Demonstrates a resource keyed by a string.
*
* @author jbetz
*/
@RestLiCollection(
name = "stringKeys",
namespace = "com.linkedin.restli.examples.greetings.client",
keyName = "parentKey"
)
public class StringKeysResource extends CollectionResourceTemplate<String, Message>
{
private static final String[] MESSAGES =
{ "I need some %20.", "Your tests run too slow.", };
private static final Tone[] TONES = { Tone.FRIENDLY, Tone.SINCERE, Tone.INSULTING };
private static final int INITIAL_SIZE = 20;
private static final String[] INITIAL_MESSAGES = new String[INITIAL_SIZE];
private static final Tone[] INITIAL_TONES = new Tone[INITIAL_SIZE];
private static final String[] TEST_KEYS = new String[] {
StringTestKeys.SIMPLEKEY, StringTestKeys.SIMPLEKEY2, StringTestKeys.SIMPLEKEY3,
StringTestKeys.URL, StringTestKeys.URL2, StringTestKeys.URL3,
StringTestKeys.SINGLE_ENCODED_URL, StringTestKeys.DOUBLE_ENCODED_URL,
StringTestKeys.COMPLICATED_KEY
};
static {
// generate some "random" initial data
for (int i = 0; i < INITIAL_SIZE; i++)
INITIAL_MESSAGES[i] = MESSAGES[i % MESSAGES.length];
for (int i = 0; i < INITIAL_SIZE; i++)
INITIAL_TONES[i] = TONES[i % TONES.length];
}
private final AtomicLong _idSeq = new AtomicLong();
private String generateId()
{
return "message" + _idSeq.getAndIncrement();
}
private final Map<String, Message> _db = Collections.synchronizedMap(new LinkedHashMap<String, Message>());
public StringKeysResource()
{
for (int i = 0; i < INITIAL_SIZE; i++)
{
Message g =
new Message().setId(generateId())
.setMessage(INITIAL_MESSAGES[i])
.setTone(INITIAL_TONES[i]);
_db.put(g.getId().toString(), g);
}
for(String key : TEST_KEYS)
{
_db.put(key, new Message().setId(key)
.setMessage(key) // echo back the key in the message
.setTone(Tone.SINCERE));
}
}
@RestMethod.Create
public CreateResponse create(Message entity)
{
_db.put(entity.getId().toString(), entity);
return new CreateResponse(entity.getId());
}
@RestMethod.BatchGet
public Map<String, Message> batchGet(Set<String> ids)
{
Map<String, Message> batch = new HashMap<String, Message>();
Map<String, RestLiServiceException> errors = new HashMap<String, RestLiServiceException>();
for (String id : ids)
{
Message g = _db.get(id);
if (g != null)
{
batch.put(id, g);
}
else
{
errors.put(id, new RestLiServiceException(HttpStatus.S_404_NOT_FOUND));
}
}
return new BatchResult<String, Message>(batch, errors);
}
@RestMethod.BatchUpdate
public BatchUpdateResult<String, Message> batchUpdate(BatchUpdateRequest<String, Message> entities)
{
Map<String, UpdateResponse> responseMap = new HashMap<String, UpdateResponse>();
for (Map.Entry<String, Message> entry : entities.getData().entrySet())
{
responseMap.put(entry.getKey(), update(entry.getKey(), entry.getValue()));
}
return new BatchUpdateResult<String, Message>(responseMap);
}
@RestMethod.BatchPartialUpdate
public BatchUpdateResult<String, Message> batchUpdate(BatchPatchRequest<String, Message> entityUpdates)
{
Map<String, UpdateResponse> responseMap = new HashMap<String, UpdateResponse>();
for (Map.Entry<String, PatchRequest<Message>> entry : entityUpdates.getData().entrySet())
{
responseMap.put(entry.getKey(), update(entry.getKey(), entry.getValue()));
}
return new BatchUpdateResult<String, Message>(responseMap);
}
@RestMethod.BatchCreate
public BatchCreateResult<String, Message> batchCreate(BatchCreateRequest<String, Message> entities)
{
List<CreateResponse> responses = new ArrayList<CreateResponse>(entities.getInput().size());
for (Message g : entities.getInput())
{
responses.add(create(g));
}
return new BatchCreateResult<String, Message>(responses);
}
@RestMethod.BatchDelete
public BatchUpdateResult<String, Message> batchDelete(BatchDeleteRequest<String, Message> deleteRequest)
{
Map<String, UpdateResponse> responseMap = new HashMap<String, UpdateResponse>();
for (String id : deleteRequest.getKeys())
{
responseMap.put(id, delete(id));
}
return new BatchUpdateResult<String, Message>(responseMap);
}
@RestMethod.Get
public Message get(String key)
{
return _db.get(key);
}
@RestMethod.Delete
public UpdateResponse delete(String key)
{
boolean removed = _db.remove(key) != null;
return new UpdateResponse(removed ? HttpStatus.S_204_NO_CONTENT : HttpStatus.S_404_NOT_FOUND);
}
@RestMethod.PartialUpdate
public UpdateResponse update(String key, PatchRequest<Message> patch)
{
Message g = _db.get(key);
if (g == null)
{
return new UpdateResponse(HttpStatus.S_404_NOT_FOUND);
}
try
{
PatchApplier.applyPatch(g, patch);
}
catch (DataProcessingException e)
{
return new UpdateResponse(HttpStatus.S_400_BAD_REQUEST);
}
_db.put(key, g);
return new UpdateResponse(HttpStatus.S_204_NO_CONTENT);
}
@RestMethod.Update
public UpdateResponse update(String key, Message entity)
{
Message g = _db.get(key);
if (g == null)
{
return new UpdateResponse(HttpStatus.S_404_NOT_FOUND);
}
_db.put(key, entity);
return new UpdateResponse(HttpStatus.S_204_NO_CONTENT);
}
@Finder("search")
public List<Message> search(@PagingContextParam PagingContext ctx, @QueryParam("keyword") @Optional String keyword)
{
keyword = keyword.toLowerCase();
List<Message> messages = new ArrayList<Message>();
int idx = 0;
int start = ctx.getStart();
int stop = start + ctx.getCount();
for (Message g : _db.values())
{
if (keyword == null || g.getMessage().toLowerCase().contains(keyword))
{
if (idx++ >= ctx.getStart())
{
messages.add(g);
}
if (idx == stop)
{
break;
}
}
}
return messages;
}
}