Package com.linkedin.restli.example

Source Code of com.linkedin.restli.example.RestLiExampleBasicClient

/*
   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.example;


import com.linkedin.common.callback.Callback;
import com.linkedin.common.callback.FutureCallback;
import com.linkedin.common.util.None;
import com.linkedin.r2.RemoteInvocationException;
import com.linkedin.r2.transport.common.Client;
import com.linkedin.r2.transport.common.bridge.client.TransportClient;
import com.linkedin.r2.transport.common.bridge.client.TransportClientAdapter;
import com.linkedin.r2.transport.http.client.HttpClientFactory;
import com.linkedin.restli.client.FindRequest;
import com.linkedin.restli.client.Request;
import com.linkedin.restli.client.Response;
import com.linkedin.restli.client.ResponseFuture;
import com.linkedin.restli.client.RestClient;
import com.linkedin.restli.client.response.CreateResponse;
import com.linkedin.restli.client.util.PatchGenerator;
import com.linkedin.restli.common.CollectionResponse;
import com.linkedin.restli.common.EmptyRecord;
import com.linkedin.restli.common.PatchRequest;
import com.linkedin.restli.example.photos.AlbumEntryBuilders;
import com.linkedin.restli.example.photos.AlbumsBuilders;
import com.linkedin.restli.example.photos.PhotosBuilders;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


/**
* @author Keren Jin
*/
public class RestLiExampleBasicClient
{
  /**
   * This is a stand-alone app to demo the use of client-side Pegasus API. To run in,
   * com.linkedin.restli.example.RestLiExamplesServer has to be running.
   *
   * The only argument is the path to the resource on the photo server, e.g. /photos/1
   */
  public static void main(String[] args) throws Exception
  {
    // create HTTP Netty client with default properties
    final HttpClientFactory http = new HttpClientFactory();
    final TransportClient transportClient = http.getClient(Collections.<String, String>emptyMap());
    // create an abstraction layer over the actual client, which supports both REST and RPC
    final Client r2Client = new TransportClientAdapter(transportClient);
    // REST client wrapper that simplifies the interface
    final StringBuilder serverUrlBuilder = new StringBuilder("http://").append(SERVER_HOSTNAME).append(":").append(SERVER_PORT).append("/");
    final RestClient restClient = new RestClient(r2Client, serverUrlBuilder.toString());
    final RestLiExampleBasicClient photoClient = new RestLiExampleBasicClient(restClient);

    String pathInfo = args.length == 0 ? "" : args[0];
    photoClient.sendRequest(pathInfo, new PrintWriter(System.out));
    photoClient.shutdown();
    http.shutdown(new FutureCallback<None>());
  }

  public RestLiExampleBasicClient(RestClient restClient)
  {
    _restClient = restClient;
  }

  public void sendRequest(String pathInfo, PrintWriter respWriter)
  {
    /* Supported URLs
     *  /fail: just fail
     *  /album/<album_id>: display album
     *  all others: make photos, get photos, purge photos
     */
    try
    {
      if (pathInfo.equals("/fail"))
      {
        getNonPhoto();
      }
      else
      {
        if (pathInfo.startsWith("/album/"))
        {
          getAlbum(respWriter, Long.parseLong(pathInfo.substring("/album/".length())));
        }
        else
        {
          // this track does not make any assumption on server
          // we need to create photo first, find it and clean them up
          // we use both sync and async approaches for creating

          final long newPhotoId = createPhoto(respWriter);
          final CountDownLatch latch = new CountDownLatch(1);
          createPhotoAsync(respWriter, latch, newPhotoId);
          getPhoto(respWriter, newPhotoId);
          findPhoto(respWriter);
          partialUpdatePhoto(respWriter, newPhotoId);
          // photos and albums have IDs starting from 1
          getAlbumSummary(respWriter, (long) new Random().nextInt(10) + 1);
          purgeAllPhotos(respWriter);
          try
          {
            latch.await();
          }
          catch (InterruptedException e)
          {
            respWriter.println(e.getMessage());
          }
        }
      }
    }
    catch (RemoteInvocationException e)
    {
      respWriter.println("Error in example client: " + e.getMessage());
    }
    respWriter.flush();
  }

  public void shutdown()
  {
    _restClient.shutdown(new Callback<None>()
    {
      @Override
      public void onError(Throwable e)
      {
        throw new RuntimeException("Error occurred during example client shutdown.", e);
      }

      @Override
      public void onSuccess(None result)
      {
        System.out.println("Example client is shutdown.");
      }
    });
  }

  private long createPhoto(PrintWriter respWriter) throws RemoteInvocationException
  {
    // make create photo request and send with the rest client synchronously
    // response of create request does not have body, therefore use EmptyRecord as template

    // create an instance of photo pragmatically
    // this resembles to photo-create.json
    final LatLong newLatLong = new LatLong().setLatitude(37.42394f).setLongitude(-122.0708f);
    final EXIF newExif = new EXIF().setLocation(newLatLong);
    final Photo newPhoto = new Photo().setTitle("New Photo").setFormat(PhotoFormats.PNG).setExif(newExif);

    final Request<EmptyRecord> createReq1 = _photoBuilders.create().input(newPhoto).build();
    final ResponseFuture<EmptyRecord> createFuture1 = _restClient.sendRequest(createReq1);
    // Future.getResource() blocks until server responds
    final Response<EmptyRecord> createResp1 = createFuture1.getResponse();
    EmptyRecord entity1 = createResp1.getEntity();

    @SuppressWarnings("unchecked")
    final CreateResponse<Long> entity = (CreateResponse<Long>)createResp1.getEntity();
    final long newPhotoId = entity.getId();
    respWriter.println("New photo ID: " + newPhotoId);

    return newPhotoId;
  }

  /**
   * make create photo request and send with the rest client asynchronously
   */
  private void createPhotoAsync(final PrintWriter respWriter, final CountDownLatch latch, final long newPhotoId)
  {
    // this resembles to photo-create-id.json
    final LatLong newLatLong = new LatLong().setLatitude(40.725f).setLongitude(-74.005f);
    final EXIF newExif = new EXIF().setIsFlash(false).setLocation(newLatLong);
    final Photo newPhoto = new Photo().setTitle("Updated Photo").setFormat(PhotoFormats.JPG).setExif(newExif);

    final Request<EmptyRecord> createReq2 = _photoBuilders.update().id(newPhotoId).input(newPhoto).build();

    // send request with callback
    _restClient.sendRequest(createReq2, new Callback<Response<EmptyRecord>>()
    {
      @Override
      public void onError(Throwable e)
      {
        respWriter.println(e.getMessage());
        latch.countDown();
      }

      @Override
      public void onSuccess(Response<EmptyRecord> result)
      {
        respWriter.println("Update photo is successful: " + (result.getStatus() == 204));

        // without a condition variable or CountDownLatch,
        // this callback might not be called before main thread terminates
        latch.countDown();
      }
    });
  }

  /**
   * Retrieve the album information and each photo in the album. The photos are retrieved in parallel.
   */
  private void getAlbum(PrintWriter respWriter, long albumId) throws RemoteInvocationException
  {
    // get the specific album
    final Request<Album> getAlbumReq = _albumBuilders.get().id(albumId).build();
    final ResponseFuture<Album> getAlbumFuture = _restClient.sendRequest(getAlbumReq);
    final Response<Album> getResp = getAlbumFuture.getResponse();
    final Album album = getResp.getEntity();

    respWriter.println(album.getTitle());
    respWriter.println("Created on " + new Date(album.getCreationTime()));

    // get the album's entries
    final FindRequest<AlbumEntry> searchReq = _albumEntryBuilders.findBySearch().albumIdParam(albumId).build();
    final ResponseFuture<CollectionResponse<AlbumEntry>> responseFuture = _restClient.sendRequest(searchReq);
    final Response<CollectionResponse<AlbumEntry>> response = responseFuture.getResponse();
    final List<AlbumEntry> entries = new ArrayList<AlbumEntry>(response.getEntity().getElements());

    entries.add(new AlbumEntry().setAlbumId(-1).setPhotoId(9999));

    // don't return until all photo requests done
    final CountDownLatch latch = new CountDownLatch(entries.size());

    // fetch every photo asynchronously
    // store either a photo or an exception
    final Object[] photos = new Object[entries.size()];
    for (int i = 0; i < entries.size(); i++)
    {
      final int finalI = i; // need final version for callback
      final AlbumEntry entry = entries.get(i);
      final long photoId = entry.getPhotoId();
      final Request<Photo> getPhotoReq = _photoBuilders.get().id(photoId).build();
      _restClient.sendRequest(getPhotoReq, new Callback<Response<Photo>>()
      {
        @Override
        public void onSuccess(Response<Photo> result)
        {
          photos[finalI] = result.getEntity();
          latch.countDown();
        }

        @Override
        public void onError(Throwable e)
        {
          photos[finalI] = e;
        }
      });
    }

    try
    {
      // wait for all requests to finish
      latch.await(2, TimeUnit.SECONDS);
      if (latch.getCount() > 0)
      {
        respWriter.println("Failed to retrieve some photo(s)");
      }
    }
    catch (InterruptedException e)
    {
      e.printStackTrace(respWriter);
    }

    // print photo data
    for (int i = 0; i < entries.size(); i++)
    {
      final Object val = photos[i];
      final AlbumEntry entry = entries.get(i);
      if (val instanceof Throwable)
      {
        respWriter.println("Failed to load photo " + entry.getPhotoId());
        respWriter.println("Stack trace:");
        ((Throwable) val).printStackTrace(respWriter);
        respWriter.println();
      }
      else if (val instanceof Photo)
      {
        final Photo photo = (Photo) val;
        respWriter.println("Photo " + photo.getTitle() + ":");
        respWriter.println(photo);
        respWriter.println("Added on " + new Date(entry.getAddTime()));
      }
      else
      {
        throw new AssertionError("expected photo or exception");
      }
    }
  }

  /**
   * send request to retrieve created photo
   */
  private void getPhoto(PrintWriter respWriter, long newPhotoId) throws RemoteInvocationException
  {
    final Request<Photo> getReq = _photoBuilders.get().id(newPhotoId).build();
    final ResponseFuture<Photo> getFuture = _restClient.sendRequest(getReq);
    final Response<Photo> getResp = getFuture.getResponse();
    respWriter.println("Photo: " + getResp.getEntity().toString());
  }

  /**
   * call action purge to delete all photos on server
   */
  private void purgeAllPhotos(PrintWriter respWriter) throws RemoteInvocationException
  {
    final Request<Integer> purgeReq = _photoBuilders.actionPurge().build();
    final ResponseFuture<Integer> purgeFuture = _restClient.sendRequest(purgeReq);
    final Response<Integer> purgeResp = purgeFuture.getResponse();
    respWriter.println("Purged " + purgeResp.getEntity() + " photos");
  }

  /**
   * retrieve album
   */
  private void getAlbumSummary(PrintWriter respWriter, long newAlbumId) throws RemoteInvocationException
  {
    final Request<Album> getReq = _albumBuilders.get().id(newAlbumId).build();
    final ResponseFuture<Album> getFuture = _restClient.sendRequest(getReq);
    final Response<Album> getResp = getFuture.getResponse();
    respWriter.println("Album: " + getResp.getEntity().toString());
  }

  /**
   * failed request that try to access non-existing photo and throw RestException
   */
  private void getNonPhoto() throws RemoteInvocationException
  {
    final Request<Photo> failReq = _photoBuilders.get().id(-1L).build();
    final ResponseFuture<Photo> failFuture = _restClient.sendRequest(failReq);
    final Response<Photo> failResponse = failFuture.getResponse();
  }

  /**
   *
   */
  private void partialUpdatePhoto(PrintWriter respWriter, long photoId) throws RemoteInvocationException
  {
    final Request<Photo> getReq = _photoBuilders.get().id(photoId).build();
    final ResponseFuture<Photo> getFuture = _restClient.sendRequest(getReq);
    final Response<Photo> getResp = getFuture.getResponse();
    final Photo originalPhoto = getResp.getEntity();

    final Photo updatedPhoto = new Photo().setTitle("Partially Updated Photo");
    final PatchRequest<Photo> patch = PatchGenerator.diff(originalPhoto, updatedPhoto);

    final Request<EmptyRecord> partialUpdateRequest = _photoBuilders.partialUpdate().id(photoId).input(patch).build();
    final int status = _restClient.sendRequest(partialUpdateRequest).getResponse().getStatus();
    respWriter.println("Partial update photo is successful: " + (status == 202));
  }

  /**
   * use Finder to find the photo with some criteria
   */
  private void findPhoto(PrintWriter respWriter) throws RemoteInvocationException
  {
    final long newPhotoId = createPhoto(respWriter);
    createPhoto(respWriter);
    createPhoto(respWriter);

    final Request<Photo> getReq = _photoBuilders.get().id(newPhotoId).build();
    final ResponseFuture<Photo> getFuture = _restClient.sendRequest(getReq);
    final Response<Photo> getResp = getFuture.getResponse();
    final Photo photo = getResp.getEntity();

    final FindRequest<Photo> findReq = _photoBuilders
        .findByTitleAndOrFormat()
        .titleParam(photo.getTitle())
        .formatParam(photo.getFormat())
        .build();

    final CollectionResponse<Photo> crPhotos = _restClient.sendRequest(findReq).getResponse().getEntity();
    final List<Photo> photos = crPhotos.getElements();

    respWriter.println("Found " + photos.size() + " photos with title " + photo.getTitle());
  }

  private static final String SERVER_HOSTNAME = "localhost";
  private static final int SERVER_PORT = 7279;

  // builder is convenient way to generate rest.li request
  private static final PhotosBuilders _photoBuilders          = new PhotosBuilders();
  private static final AlbumsBuilders _albumBuilders          = new AlbumsBuilders();
  private static final AlbumEntryBuilders _albumEntryBuilders = new AlbumEntryBuilders();

  private final RestClient _restClient;
}
TOP

Related Classes of com.linkedin.restli.example.RestLiExampleBasicClient

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.