1 // Copyright 2011 Google Inc. All Rights Reserved.
3 package com
.google
.appengine
.api
.prospectivesearch
;
5 import com
.google
.appengine
.api
.datastore
.Entity
;
6 import com
.google
.appengine
.api
.datastore
.EntityTranslator
;
7 import com
.google
.appengine
.api
.prospectivesearch
.ProspectiveSearchPb
.*;
8 import com
.google
.apphosting
.api
.ApiProxy
;
9 import com
.google
.common
.base
.CharMatcher
;
10 import com
.google
.common
.io
.BaseEncoding
;
11 import com
.google
.io
.protocol
.ProtocolMessage
;
13 import java
.util
.ArrayList
;
14 import java
.util
.List
;
16 import java
.util
.logging
.Level
;
17 import java
.util
.logging
.Logger
;
19 import javax
.servlet
.http
.HttpServletRequest
;
22 * The ProspectiveSearchServiceImpl class is a proxy to a remote
23 * Prospective Search service. Calls are dispatched via AppEngine's
24 * ApiProxy system. All responses are checked for validity or a
25 * RuntimeException is thrown.
28 class ProspectiveSearchServiceImpl
implements ProspectiveSearchService
{
30 static final String PACKAGE
= "matcher";
32 static final Logger logger
= Logger
.getLogger(ProspectiveSearchServiceImpl
.class.getName());
35 public void subscribe(String topic
,
37 long leaseDurationSeconds
,
39 Map
<String
, FieldType
> schema
)
40 throws QuerySyntaxException
{
41 SubscribeRequest req
= new SubscribeRequest();
44 if (leaseDurationSeconds
< 0) {
45 throw new IllegalArgumentException("Lease duration must be non-negative: "
46 + leaseDurationSeconds
);
48 req
.setLeaseDurationSec(leaseDurationSeconds
);
49 req
.setVanillaQuery(query
);
51 for (Map
.Entry
<String
, FieldType
> entries
: schema
.entrySet()) {
52 SchemaEntry entry
= new SchemaEntry();
53 entry
.setName(entries
.getKey());
54 entry
.setType(entries
.getValue().internalType
);
55 req
.addSchemaEntry(entry
);
59 doCall("Subscribe", req
, new SubscribeResponse());
60 } catch (ApiProxy
.ApplicationException e
) {
61 switch (ErrorPb
.Error
.ErrorCode
.valueOf(e
.getApplicationError())) {
63 throw new QuerySyntaxException(subId
, topic
, query
, e
.getErrorDetail());
71 public void unsubscribe(String topic
, String subId
) {
72 UnsubscribeRequest req
= new UnsubscribeRequest();
77 doCall("Unsubscribe", req
, new UnsubscribeResponse());
78 } catch (ApiProxy
.ApplicationException e
) {
79 switch (ErrorPb
.Error
.ErrorCode
.valueOf(e
.getApplicationError())) {
81 throw new IllegalArgumentException(e
.getErrorDetail());
89 public void match(Entity entity
, String topic
) {
90 match(entity
, topic
, "");
94 public void match(Entity entity
, String topic
, String resultKey
) {
95 match(entity
, topic
, resultKey
,
96 DEFAULT_RESULT_RELATIVE_URL
,
97 DEFAULT_RESULT_TASK_QUEUE_NAME
,
98 DEFAULT_RESULT_BATCH_SIZE
,
103 public void match(Entity entity
,
107 String taskQueueName
,
109 boolean resultReturnDocument
) {
110 MatchRequest req
= new MatchRequest();
111 req
.setDocument(EntityTranslator
.convertToPb(entity
));
113 req
.setResultKey(resultKey
);
114 req
.setResultRelativeUrl(relativeUrl
);
115 req
.setResultTaskQueue(taskQueueName
);
116 req
.setResultBatchSize(batchSize
);
117 if (resultReturnDocument
) {
118 req
.setResultPythonDocumentClass(MatchRequest
.PythonDocumentClass
.ENTITY
);
121 doCall("Match", req
, new MatchResponse());
125 public List
<Subscription
> listSubscriptions(String topic
) {
126 return listSubscriptions(topic
, "",
127 DEFAULT_LIST_SUBSCRIPTIONS_MAX_RESULTS
,
132 public List
<Subscription
> listSubscriptions(String topic
,
135 long expiresBefore
) {
136 ListSubscriptionsRequest req
= new ListSubscriptionsRequest();
138 req
.setSubscriptionIdStart(subIdStart
);
139 req
.setMaxResults(maxResults
);
140 if (expiresBefore
> 0) {
141 req
.setExpiresBefore(expiresBefore
);
143 ListSubscriptionsResponse rsp
= new ListSubscriptionsResponse();
145 doCall("ListSubscriptions", req
, rsp
);
146 return convertSubscriptionList(rsp
.subscriptions());
150 public Subscription
getSubscription(String topic
, String subId
) {
151 List
<Subscription
> subs
= listSubscriptions(topic
, subId
, 1, 0);
152 if (!subs
.isEmpty()) {
153 Subscription sub
= subs
.get(0);
154 if (sub
.getId().equals(subId
)) {
158 throw new IllegalArgumentException(String
.format("No such subscription topic: %s, id: %s",
163 public List
<String
> listTopics(String topicStart
, long maxResults
) {
164 ListTopicsRequest req
= new ListTopicsRequest();
165 if (!topicStart
.equals("")) {
166 req
.setTopicStart(topicStart
);
168 req
.setMaxResults(maxResults
);
169 ListTopicsResponse rsp
= new ListTopicsResponse();
170 doCall("ListTopics", req
, rsp
);
175 public Entity
getDocument(HttpServletRequest matchCallbackPost
) {
177 String docStr
= matchCallbackPost
.getParameter("document");
178 byte[] docBuf
= BaseEncoding
.base64Url().decode(CharMatcher
.WHITESPACE
.removeFrom(docStr
));
179 return EntityTranslator
.createFromPbBytes(docBuf
);
180 } catch (IllegalArgumentException e
) {
181 logger
.log(Level
.SEVERE
, "Could not decode returned matching message.", e
);
186 static void doCall(String name
, ProtocolMessage
<?
> request
, ProtocolMessage
<?
> response
) {
187 byte[] serializedResponse
= ApiProxy
.makeSyncCall(PACKAGE
, name
, request
.toByteArray());
188 if (!response
.mergeFrom(serializedResponse
)) {
189 throw new ApiProxy
.ArgumentException(PACKAGE
, name
);
194 * Converts List<SubscriptionRecord> to List<Subscription>.
196 static List
<Subscription
> convertSubscriptionList(List
<SubscriptionRecord
> from
) {
197 List
<Subscription
> to
= new ArrayList
<Subscription
>();
198 for (SubscriptionRecord internal
: from
) {
199 to
.add(new Subscription(internal
));