Your SlideShare is downloading. ×
0
Developing Cloud Computing Applications with Java Shlomo Swidler CTO, MyDrifts.com [email_address]
Developing Cloud Computing Applications with Java <ul><li>Overview of Cloud Computing </li></ul><ul><li>Amazon’s Cloud Pla...
About Me <ul><li>CTO & co-Founder  </li></ul><ul><ul><li>Music marketing on social networks </li></ul></ul><ul><ul><li>Pat...
Cloud Computing Is… <ul><li>A style of computing in which dynamically scalable and often virtualized resources are provide...
Cloud Computing Is… <ul><li>A style of  computing  in which dynamically scalable and often virtualized  resources  are pro...
Cloud Computing Is… 22 June 2009 Developing Cloud Computing Applications with Java  by Shlomo Swidler
Cloud Computing Is… 22 June 2009 Developing Cloud Computing Applications with Java  by Shlomo Swidler
Cloud Computing Is… <ul><li>Computing Resources </li></ul><ul><li>As a Service </li></ul><ul><ul><li>Pay-as-you-go or Subs...
Advantages of Cloud Computing <ul><li>From a Developer’s Perspective: </li></ul><ul><li>Pay-as-you-go “utility computing” ...
Risks of Cloud Computing <ul><li>Security </li></ul><ul><ul><li>Who else has access to “your” resources ? </li></ul></ul><...
Amazon’s Cloud Platform: Amazon Web Services <ul><li>Infrastructure-as-a-Service </li></ul><ul><li>Processors & Memory </l...
Amazon Dashboard <ul><li>ElasticFox Firefox plugin </li></ul>22 June 2009 Developing Cloud Computing Applications with Jav...
Developing on Amazon’s Cloud <ul><li>Standard stuff: </li></ul><ul><li>Language </li></ul><ul><li>Libraries </li></ul><ul>...
Google’s Cloud Platform: Google App Engine 22 June 2009 Developing Cloud Computing Applications with Java  by Shlomo Swidl...
Google Dashboard <ul><li>Google Administration Console </li></ul>22 June 2009 Developing Cloud Computing Applications with...
Developing on Google’s Cloud <ul><li>Easy stuff: </li></ul><ul><li>Scaling </li></ul><ul><li>Challenges: </li></ul><ul><li...
Application Development Challenges Posed by the Cloud <ul><li>Deploying to the Cloud </li></ul><ul><li>Designing for Scala...
Application Development Challenges Posed by the Cloud <ul><li>Deploying to the Cloud </li></ul><ul><li>Designing for Scala...
Deploying to the Cloud <ul><li>IaaS platforms </li></ul><ul><ul><li>Mostly the same as traditional deployment </li></ul></...
Deploying an Application to Google App Engine 22 June 2009 Developing Cloud Computing Applications with Java  by Shlomo Sw...
Designing the Application Tier for Scalability 22 June 2009 Developing Cloud Computing Applications with Java  by Shlomo S...
Designing the Application Tier for Scalability <ul><li>Make sure your Storage Tier is optimized </li></ul><ul><ul><li>Opti...
Parallelize with Concurrent Threads <ul><li>Motivation </li></ul><ul><ul><li>Allow long-running tasks to proceed without i...
java.util.concurrent  Example: In-Memory Cache <ul><li>Existing implementations such as memcached </li></ul><ul><li>Cache ...
java.util.concurrent  Example: In-Memory Cache <ul><li>String userId =  &quot;visitor01&quot; ; </li></ul><ul><li>String m...
java.util.concurrent  Example: In-Memory Cache <ul><li>import  java.util.concurrent.Callable; </li></ul><ul><li>import  ja...
Parallelize with Service Pools <ul><li>Motivation </li></ul><ul><ul><li>Allow services to scale according to demand </li><...
Java Service Pool for Amazon Web Services: Lifeguard <ul><li>Open source Apache License Version 2.0 </li></ul><ul><ul><li>...
Lifeguard Framework <ul><li>Framework provides: </li></ul><ul><ul><li>Message handling </li></ul></ul><ul><ul><li>File han...
Lifeguard Framework <ul><li>You provide: </li></ul><ul><ul><li>Ingestor </li></ul></ul><ul><ul><li>Service </li></ul></ul>...
<ul><li>public class  ResizeImageIngestor  extends  IngestorBase { </li></ul><ul><ul><li>private static final  String  Res...
<ul><li>public class  ResizeImageService  extends  AbstractBaseService { </li></ul><ul><li>private static final  String  S...
<ul><li><ServicePool> </li></ul><ul><ul><li><ServiceName> ResizeImage </ServiceName> </li></ul></ul><ul><ul><li><VMImage> ...
Service Pool <ul><li>Pool of service instances dynamically scales with load </li></ul>22 June 2009 Developing Cloud Comput...
Service Pool <ul><li>Pool of service instances dynamically scales with load </li></ul>22 June 2009 Developing Cloud Comput...
Service Pool <ul><li>Multiple service pools scale independently </li></ul>22 June 2009 Developing Cloud Computing Applicat...
Service Pool <ul><li>Workloads can follow different workflows </li></ul><ul><ul><li>Specify the Ingestor’s XML accordingly...
Developing Cloud Computing Applications with Java <ul><ul><ul><li>Q&A </li></ul></ul></ul>22 June 2009 Developing Cloud Co...
Developing Cloud Computing Applications with Java <ul><ul><ul><li>Shlomo Swidler </li></ul></ul></ul><ul><ul><ul><li>[emai...
Upcoming SlideShare
Loading in...5
×

Java Tech Day 2009 - Developing Cloud Computing Applications With Java

9,005

Published on

Challenges faced by developers of cloud-computing applications and Java-based solutions. Including an overview of Google App Engine, Amazon Web Services, and sample Java code demonstrating design patterns that will scale in the cloud.

Published in: Technology, News & Politics
0 Comments
12 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
9,005
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
725
Comments
0
Likes
12
Embeds 0
No embeds

No notes for slide

Transcript of "Java Tech Day 2009 - Developing Cloud Computing Applications With Java"

  1. 1. Developing Cloud Computing Applications with Java Shlomo Swidler CTO, MyDrifts.com [email_address]
  2. 2. Developing Cloud Computing Applications with Java <ul><li>Overview of Cloud Computing </li></ul><ul><li>Amazon’s Cloud Platform </li></ul><ul><li>Google’s Cloud Platform </li></ul><ul><li>Application Development Challenges Posed by the Cloud… … and Java Solutions </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  3. 3. About Me <ul><li>CTO & co-Founder </li></ul><ul><ul><li>Music marketing on social networks </li></ul></ul><ul><ul><li>Patent-pending targeting technology </li></ul></ul><ul><ul><li>Java, MySQL, auto-scaling & cloud-based </li></ul></ul><ul><li>Active in the Cloud Computing community </li></ul><ul><ul><li>Open Cloud Computing Interface Working Group (an Open Grid Forum initiative) participant </li></ul></ul><ul><ul><li>Contributor to Open Source cloud & Java projects </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  4. 4. Cloud Computing Is… <ul><li>A style of computing in which dynamically scalable and often virtualized resources are provided as a service over the Internet. Users need not have knowledge of, expertise in, or control over the technology infrastructure in the “cloud” that supports them. – Wikipedia </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  5. 5. Cloud Computing Is… <ul><li>A style of computing in which dynamically scalable and often virtualized resources are provided as a service over the Internet. Users need not have knowledge of, expertise in, or control over the technology infrastructure in the “cloud” that supports them. – Wikipedia </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  6. 6. Cloud Computing Is… 22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  7. 7. Cloud Computing Is… 22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  8. 8. Cloud Computing Is… <ul><li>Computing Resources </li></ul><ul><li>As a Service </li></ul><ul><ul><li>Pay-as-you-go or Subscription </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Infrastructure Platform Software Processor LAMP Stack Email Memory JVM CRM System Storage Python VM ERP System Network MapReduce SCM System
  9. 9. Advantages of Cloud Computing <ul><li>From a Developer’s Perspective: </li></ul><ul><li>Pay-as-you-go “utility computing” </li></ul><ul><ul><li>Saves time </li></ul></ul><ul><ul><li>Saves $$$ </li></ul></ul><ul><li>On-demand resource allocation & release </li></ul><ul><li>Scalability </li></ul><ul><ul><li>More on this later </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  10. 10. Risks of Cloud Computing <ul><li>Security </li></ul><ul><ul><li>Who else has access to “your” resources ? </li></ul></ul><ul><li>Recovery </li></ul><ul><ul><li>How easy is it ? </li></ul></ul><ul><li>Provider lock-in </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  11. 11. Amazon’s Cloud Platform: Amazon Web Services <ul><li>Infrastructure-as-a-Service </li></ul><ul><li>Processors & Memory </li></ul><ul><ul><li>Elastic Compute Cloud “EC2” </li></ul></ul><ul><li>Storage </li></ul><ul><ul><li>Simple Storage Service “S3” </li></ul></ul><ul><ul><li>Elastic Block Store “EBS” </li></ul></ul><ul><ul><li>SimpleDB database </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler <ul><li>Network </li></ul><ul><ul><li>Content Delivery Network CloudFront </li></ul></ul><ul><li>Messaging </li></ul><ul><ul><li>Simple Queue Service “SQS” </li></ul></ul>
  12. 12. Amazon Dashboard <ul><li>ElasticFox Firefox plugin </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  13. 13. Developing on Amazon’s Cloud <ul><li>Standard stuff: </li></ul><ul><li>Language </li></ul><ul><li>Libraries </li></ul><ul><li>Communications </li></ul><ul><li>Web Servers </li></ul><ul><li>Application Servers </li></ul><ul><li>Databases </li></ul><ul><li>Challenges: </li></ul><ul><li>Scaling </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler <ul><li>Suitable for existing applications </li></ul>
  14. 14. Google’s Cloud Platform: Google App Engine 22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler <ul><li>Platform-as-a-Service </li></ul><ul><li>Language </li></ul><ul><ul><li>Python </li></ul></ul><ul><ul><li>Java </li></ul></ul><ul><li>Storage </li></ul><ul><ul><li>JDO or JPA or Datastore APIs </li></ul></ul><ul><li>User Accounts </li></ul><ul><li>Email </li></ul><ul><li>Image Transformation </li></ul><ul><li>Memcached </li></ul><ul><li>Cron jobs </li></ul>
  15. 15. Google Dashboard <ul><li>Google Administration Console </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  16. 16. Developing on Google’s Cloud <ul><li>Easy stuff: </li></ul><ul><li>Scaling </li></ul><ul><li>Challenges: </li></ul><ul><li>Language </li></ul><ul><li>Libraries </li></ul><ul><li>Communications </li></ul><ul><li>Data Storage </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler <ul><li>Suitable for new, lighter-weight applications </li></ul>
  17. 17. Application Development Challenges Posed by the Cloud <ul><li>Deploying to the Cloud </li></ul><ul><li>Designing for Scalability </li></ul><ul><ul><li>Web Tier </li></ul></ul><ul><ul><li>Application Tier </li></ul></ul><ul><ul><li>Database Tier </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  18. 18. Application Development Challenges Posed by the Cloud <ul><li>Deploying to the Cloud </li></ul><ul><li>Designing for Scalability </li></ul><ul><ul><li>Web Tier </li></ul></ul><ul><ul><li>Application Tier </li></ul></ul><ul><ul><li>Database Tier </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  19. 19. Deploying to the Cloud <ul><li>IaaS platforms </li></ul><ul><ul><li>Mostly the same as traditional deployment </li></ul></ul><ul><li>PaaS & SaaS platforms </li></ul><ul><ul><li>Custom procedures </li></ul></ul><ul><ul><li>Custom configurations </li></ul></ul><ul><ul><li>Custom tools </li></ul></ul><ul><ul><li>Google App Engine: Eclipse plug-in </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  20. 20. Deploying an Application to Google App Engine 22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  21. 21. Designing the Application Tier for Scalability 22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  22. 22. Designing the Application Tier for Scalability <ul><li>Make sure your Storage Tier is optimized </li></ul><ul><ul><li>Optimize database queries </li></ul></ul><ul><ul><li>Use in-memory caching </li></ul></ul><ul><li>Parallelize operations </li></ul><ul><ul><li>Use concurrent threads </li></ul></ul><ul><ul><li>Use the Service Pools design pattern </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  23. 23. Parallelize with Concurrent Threads <ul><li>Motivation </li></ul><ul><ul><li>Allow long-running tasks to proceed without impacting performance </li></ul></ul><ul><li>Java offers the java.util.concurrent package </li></ul><ul><ul><li>Executor interface </li></ul></ul><ul><ul><li>Future<T> interface </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  24. 24. java.util.concurrent Example: In-Memory Cache <ul><li>Existing implementations such as memcached </li></ul><ul><li>Cache shared by all application instances </li></ul><ul><ul><li>Access is via the network </li></ul></ul><ul><li>Application requests an Object from the cache </li></ul><ul><li>Time until response is received can vary </li></ul><ul><ul><li>True of any network operation </li></ul></ul><ul><li>Don’t let application code wait… </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  25. 25. java.util.concurrent Example: In-Memory Cache <ul><li>String userId = &quot;visitor01&quot; ; </li></ul><ul><li>String memcachedKey = &quot;userId:&quot; + userId + &quot;.lastLoginDate&quot; ; </li></ul><ul><li>Future<Date> lastLoginDateGetter = MemcachedClient. get( memcachedKey, Date. class ); </li></ul><ul><li>// perform the rest of the request handling code here </li></ul><ul><li>// then, at the end, get the user's last login date </li></ul><ul><li>Date lastLoginDate = null ; </li></ul><ul><li>try { </li></ul><ul><li>lastLoginDate = lastLoginDateGetter.get(50, TimeUnit. MILLISECONDS); </li></ul><ul><li>} catch (InterruptedException e) { </li></ul><ul><li>// someone interrupted the FutureTask </li></ul><ul><li>} catch (ExecutionException e) { </li></ul><ul><li>// the FutureTask threw an exception </li></ul><ul><li>} catch (TimeoutException e) { </li></ul><ul><li>// the FutureTask didn't complete within the 50ms time limit </li></ul><ul><li>lastLoginDateGetter.cancel( false ); </li></ul><ul><li>} </li></ul><ul><li>// return lastLoginDate to the presentation layer </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  26. 26. java.util.concurrent Example: In-Memory Cache <ul><li>import java.util.concurrent.Callable; </li></ul><ul><li>import java.util.concurrent.Executor; </li></ul><ul><li>import java.util.concurrent.Executors; </li></ul><ul><li>import java.util.concurrent.Future; </li></ul><ul><li>import java.util.concurrent.FutureTask; </li></ul><ul><li>public class MemcachedClient { </li></ul><ul><li>private static Executor executor = Executors .newFixedThreadPool (1); </li></ul><ul><li>public static <T> Future<T> get(String objectKey, Class<T> type) { </li></ul><ul><li>final String objectKeyFinal = objectKey; </li></ul><ul><li>FutureTask<T> getFromCacheOperation = new FutureTask<T>( </li></ul><ul><li>new Callable<T>() { </li></ul><ul><li>public T call() { </li></ul><ul><li>Object networkResponse = requestObjectOverNetwork (objectKeyFinal); </li></ul><ul><li>return (T) networkResponse; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>); </li></ul><ul><li>executor . execute(getFromCacheOperation); </li></ul><ul><li>return getFromCacheOperation; </li></ul><ul><li>} </li></ul><ul><li>private static Object requestObjectOverNetwork(String objectKey) { </li></ul><ul><li>// network stuff goes in here </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  27. 27. Parallelize with Service Pools <ul><li>Motivation </li></ul><ul><ul><li>Allow services to scale according to demand </li></ul></ul><ul><ul><li>Scale up and down </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Request Queue Response Queue Storage
  28. 28. Java Service Pool for Amazon Web Services: Lifeguard <ul><li>Open source Apache License Version 2.0 </li></ul><ul><ul><li>http://code.google.com/p/lifeguard/ </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Request Queue Response Queue Storage
  29. 29. Lifeguard Framework <ul><li>Framework provides: </li></ul><ul><ul><li>Message handling </li></ul></ul><ul><ul><li>File handling </li></ul></ul><ul><ul><li>Scaling logic </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Request SQS Queue Response SQS Queue EC2 Instances Ingestor Listener S3  Storage Pool Mgr Config
  30. 30. Lifeguard Framework <ul><li>You provide: </li></ul><ul><ul><li>Ingestor </li></ul></ul><ul><ul><li>Service </li></ul></ul><ul><ul><li>Pool Manager Configuration </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Request SQS Queue Response SQS Queue EC2 Instances Ingestor Listener S3  Storage Pool Mgr Config
  31. 31. <ul><li>public class ResizeImageIngestor extends IngestorBase { </li></ul><ul><ul><li>private static final String ResizeImageWorkflowXML = </li></ul></ul><ul><ul><ul><li>&quot;<Workflow>&quot; + </li></ul></ul></ul><ul><ul><ul><ul><li>&quot;<Service>&quot; + </li></ul></ul></ul></ul><ul><ul><ul><ul><li>&quot;<Name>ResizeImage</Name>&quot; + </li></ul></ul></ul></ul><ul><ul><ul><ul><li>&quot;<WorkQueue>ResizeImage-input</WorkQueue>&quot; + </li></ul></ul></ul></ul><ul><ul><ul><ul><li>&quot;</Service>&quot; + </li></ul></ul></ul></ul><ul><ul><ul><li>&quot;</Workflow>&quot; ; </li></ul></ul></ul><ul><li>public ResizeImageIngestor() { </li></ul><ul><li>super ( ResizeImageIngestorWorkflowXML ); </li></ul><ul><li>} </li></ul><ul><ul><li>public void ingest(File imageFile) { </li></ul></ul><ul><ul><ul><li>super .ingest(Collections. singletonList (imageFile)); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Ingestor Implement the Ingestor S3  Storage
  32. 32. <ul><li>public class ResizeImageService extends AbstractBaseService { </li></ul><ul><li>private static final String ServiceConfigXML = </li></ul><ul><li>&quot;<ServiceConfig>&quot; + </li></ul><ul><li>&quot;<ServiceName>ResizeImage</ServiceName>&quot; + </li></ul><ul><li>&quot;<WorkQueue>ResizeImage-input</WorkQueue>&quot; + </li></ul><ul><li>&quot;</ServiceConfig>&quot; ; </li></ul><ul><li>public ResizeImageService() { </li></ul><ul><li>super( ServiceConfigXML ); </li></ul><ul><li>} </li></ul><ul><ul><li>public List<File> executeService(File imageFile) { </li></ul></ul><ul><ul><ul><li>Image origImage = new Image(imageFile); </li></ul></ul></ul><ul><ul><ul><li>File resizedImageFile = resizeImageToFile(origImage); </li></ul></ul></ul><ul><ul><ul><li>return Collections. singletonList (resizedImageFile); </li></ul></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Implement the Service S3  Storage
  33. 33. <ul><li><ServicePool> </li></ul><ul><ul><li><ServiceName> ResizeImage </ServiceName> </li></ul></ul><ul><ul><li><VMImage> ami-39ba5df0 </VMImage> </li></ul></ul><ul><ul><li><WorkQueue> ResizeImage-input </WorkQueue> </li></ul></ul><ul><ul><li><RampUpInterval> 1 </RampUpInterval> </li></ul></ul><ul><ul><li><RampUpDelay> 360 </RampUpDelay> </li></ul></ul><ul><ul><li><RampDownInterval> 1 </RampDownInterval> </li></ul></ul><ul><ul><li><RampDownDelay> 480 </RampDownDelay> </li></ul></ul><ul><ul><li><MinSize> 0 </MinSize> </li></ul></ul><ul><ul><li><MaxSize> 20 </MaxSize> </li></ul></ul><ul><ul><li><QueueSizeFactor> 20000 </QueueSizeFactor> </li></ul></ul><ul><li></ServicePool> </li></ul><ul><li>This configuration defines the SLA for this service </li></ul><ul><li>That’s all there is to implement </li></ul><ul><li>The framework does all the rest </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Configure the Pool Manager Pool Mgr Config
  34. 34. Service Pool <ul><li>Pool of service instances dynamically scales with load </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler Request SQS Queue Response SQS Queue EC2 Instances Ingestor Listener S3  Storage Pool Mgr Config
  35. 35. Service Pool <ul><li>Pool of service instances dynamically scales with load </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  36. 36. Service Pool <ul><li>Multiple service pools scale independently </li></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler etc.
  37. 37. Service Pool <ul><li>Workloads can follow different workflows </li></ul><ul><ul><li>Specify the Ingestor’s XML accordingly </li></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  38. 38. Developing Cloud Computing Applications with Java <ul><ul><ul><li>Q&A </li></ul></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  39. 39. Developing Cloud Computing Applications with Java <ul><ul><ul><li>Shlomo Swidler </li></ul></ul></ul><ul><ul><ul><li>[email_address] </li></ul></ul></ul><ul><ul><ul><li>Thank you! </li></ul></ul></ul>22 June 2009 Developing Cloud Computing Applications with Java by Shlomo Swidler
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×