View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.lti.endpoint;
22  
23  import org.opencastproject.lti.service.api.LtiFileUpload;
24  import org.opencastproject.lti.service.api.LtiJob;
25  import org.opencastproject.lti.service.api.LtiService;
26  import org.opencastproject.security.api.TrustedHttpClient;
27  import org.opencastproject.security.api.UnauthorizedException;
28  import org.opencastproject.serviceregistry.api.RemoteBase;
29  import org.opencastproject.serviceregistry.api.ServiceRegistry;
30  import org.opencastproject.util.NotFoundException;
31  
32  import com.google.gson.Gson;
33  import com.google.gson.reflect.TypeToken;
34  
35  import org.apache.commons.io.IOUtils;
36  import org.apache.http.HttpResponse;
37  import org.apache.http.client.methods.HttpDelete;
38  import org.apache.http.client.methods.HttpGet;
39  import org.apache.http.client.methods.HttpPost;
40  import org.apache.http.client.methods.HttpRequestBase;
41  import org.apache.http.entity.mime.MultipartEntityBuilder;
42  import org.apache.http.entity.mime.content.InputStreamBody;
43  import org.osgi.service.component.annotations.Component;
44  import org.osgi.service.component.annotations.Reference;
45  
46  import java.io.IOException;
47  import java.io.InputStreamReader;
48  import java.nio.charset.StandardCharsets;
49  import java.util.List;
50  
51  import javax.ws.rs.core.Response;
52  
53  /**
54   * The service calling the LTI REST endpoint (for multi-node setups with LTI)
55   */
56  @Component(
57      immediate = true,
58      service = LtiService.class,
59      property = {
60          "service.description=LTI Service Remote Service"
61      }
62  )
63  public class LtiServiceRemoteImpl extends RemoteBase implements LtiService {
64    private static final Gson gson = new Gson();
65  
66    public LtiServiceRemoteImpl() {
67      super(LtiService.JOB_TYPE);
68    }
69  
70    private HttpResponse safeGetResponse(final HttpRequestBase r) {
71      final HttpResponse response = getResponse(r);
72      if (response == null) {
73        throw new RuntimeException("No response from service");
74      }
75      return response;
76    }
77  
78    @Override
79    public List<LtiJob> listJobs(final String seriesId) {
80      HttpResponse response = null;
81      try {
82        response = safeGetResponse(new HttpGet("/jobs?seriesId=" + seriesId));
83        return gson.fromJson(
84            new InputStreamReader(response.getEntity().getContent(), StandardCharsets.UTF_8),
85            new TypeToken<List<LtiJob>>() { }.getType());
86      } catch (IOException e) {
87        throw new RuntimeException("failed retrieving jobs", e);
88      } finally {
89        closeConnection(response);
90      }
91    }
92  
93    @Override
94    public void upsertEvent(
95            final LtiFileUpload file,
96            final String captions,
97            final String captionFormat,
98            final String captionLanguage,
99            final String eventId,
100           final String seriesId,
101           final String metadataJson) {
102     final MultipartEntityBuilder entity = MultipartEntityBuilder.create();
103     entity.addTextBody("isPartOf", seriesId);
104     entity.addTextBody("metadata", metadataJson);
105     if (eventId != null) {
106       entity.addTextBody("eventId", eventId);
107     }
108     if (captions != null) {
109       entity.addTextBody("captions", captions);
110     }
111     if (captionFormat != null) {
112       entity.addTextBody("captionFormat", captionFormat);
113     }
114     if (captionLanguage != null) {
115       entity.addTextBody("captionLanguage", captionLanguage);
116     }
117     if (file != null) {
118       entity.addPart(file.getSourceName(), new InputStreamBody(file.getStream(), file.getSourceName()));
119     }
120     final HttpPost post = new HttpPost("/");
121     post.setEntity(entity.build());
122     closeConnection(safeGetResponse(post));
123   }
124 
125   @Override
126   public void copyEventToSeries(final String eventId, final String seriesId) {
127     final HttpPost post = new HttpPost("/" + eventId + "/copy?seriesId=" + seriesId);
128     closeConnection(safeGetResponse(post));
129   }
130 
131   @Override
132   public String getEventMetadata(final String eventId) throws NotFoundException, UnauthorizedException {
133     HttpResponse response = null;
134     try {
135       response = safeGetResponse(new HttpGet("/" + eventId + "/metadata"));
136       if (response.getStatusLine().getStatusCode() == Response.Status.NOT_FOUND.getStatusCode()) {
137         throw new NotFoundException("event not found: " + eventId);
138       }
139       if (response.getStatusLine().getStatusCode() == Response.Status.UNAUTHORIZED.getStatusCode()) {
140         throw new UnauthorizedException("not authorized to access event with ID " + eventId);
141       }
142       return IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
143     } catch (IOException e) {
144       throw new RuntimeException("failed retrieving jobs", e);
145     } finally {
146       closeConnection(response);
147     }
148   }
149 
150   @Override
151   public String getNewEventMetadata() {
152     HttpResponse response = null;
153     try {
154       response = safeGetResponse(new HttpGet("/new/metadata"));
155       return IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
156     } catch (IOException e) {
157       throw new RuntimeException("failed retrieving jobs", e);
158     } finally {
159       closeConnection(response);
160     }
161   }
162 
163   @Override
164   public void setEventMetadataJson(final String eventId, final String metadataJson)
165           throws NotFoundException, UnauthorizedException {
166     final MultipartEntityBuilder entity = MultipartEntityBuilder.create();
167     entity.addTextBody("metadata", metadataJson);
168     final HttpPost post = new HttpPost("/" + eventId + "/metadata");
169     post.setEntity(entity.build());
170     HttpResponse response = null;
171     try {
172       response = safeGetResponse(post);
173       if (response.getStatusLine().getStatusCode() == Response.Status.NOT_FOUND.getStatusCode()) {
174         throw new NotFoundException("event not found: " + eventId);
175       }
176       if (response.getStatusLine().getStatusCode() == Response.Status.UNAUTHORIZED.getStatusCode()) {
177         throw new UnauthorizedException("not authorized to access event with ID " + eventId);
178       }
179     } finally {
180       closeConnection(response);
181     }
182   }
183 
184   @Override
185   public void delete(String eventId) {
186     final HttpDelete post = new HttpDelete("/" + eventId);
187     final HttpResponse response = getResponse(post, Response.Status.NO_CONTENT.getStatusCode());
188     if (response == null) {
189       throw new RuntimeException("No response from service");
190     }
191   }
192 
193   @Reference
194   @Override
195   public void setTrustedHttpClient(TrustedHttpClient trustedHttpClient) {
196     super.setTrustedHttpClient(trustedHttpClient);
197   }
198 
199   @Reference
200   @Override
201   public void setRemoteServiceManager(ServiceRegistry serviceRegistry) {
202     super.setRemoteServiceManager(serviceRegistry);
203   }
204 
205 }