1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.workflow.handler.notification;
23
24 import org.opencastproject.job.api.JobContext;
25 import org.opencastproject.mediapackage.MediaPackage;
26 import org.opencastproject.mediapackage.MediaPackageParser;
27 import org.opencastproject.search.api.SearchService;
28 import org.opencastproject.security.api.UnauthorizedException;
29 import org.opencastproject.serviceregistry.api.ServiceRegistry;
30 import org.opencastproject.util.NotFoundException;
31 import org.opencastproject.workflow.api.AbstractWorkflowOperationHandler;
32 import org.opencastproject.workflow.api.WorkflowInstance;
33 import org.opencastproject.workflow.api.WorkflowOperationException;
34 import org.opencastproject.workflow.api.WorkflowOperationHandler;
35 import org.opencastproject.workflow.api.WorkflowOperationInstance;
36 import org.opencastproject.workflow.api.WorkflowOperationResult;
37 import org.opencastproject.workflow.api.WorkflowOperationResult.Action;
38
39 import org.apache.http.HttpResponse;
40 import org.apache.http.NameValuePair;
41 import org.apache.http.auth.AuthScope;
42 import org.apache.http.auth.UsernamePasswordCredentials;
43 import org.apache.http.client.CredentialsProvider;
44 import org.apache.http.client.entity.UrlEncodedFormEntity;
45 import org.apache.http.client.methods.HttpPost;
46 import org.apache.http.impl.client.BasicCredentialsProvider;
47 import org.apache.http.impl.client.CloseableHttpClient;
48 import org.apache.http.impl.client.HttpClientBuilder;
49 import org.apache.http.message.BasicNameValuePair;
50 import org.osgi.service.component.annotations.Component;
51 import org.osgi.service.component.annotations.Reference;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import java.net.URI;
56 import java.net.URL;
57 import java.util.ArrayList;
58 import java.util.List;
59 import java.util.Set;
60
61
62
63
64 @Component(
65 immediate = true,
66 service = WorkflowOperationHandler.class,
67 property = {
68 "service.description=Workflow Operation that POSTs MediaPackages via HTTP",
69 "workflow.operation=post-mediapackage"
70 }
71 )
72 public class MediaPackagePostOperationHandler extends AbstractWorkflowOperationHandler {
73
74
75 private static final Logger logger = LoggerFactory.getLogger(MediaPackagePostOperationHandler.class);
76
77
78 private SearchService searchService;
79
80 @Reference
81 public void setSearchService(SearchService searchService) {
82 this.searchService = searchService;
83 }
84
85 @Reference
86 @Override
87 public void setServiceRegistry(ServiceRegistry serviceRegistry) {
88 super.setServiceRegistry(serviceRegistry);
89 }
90
91 public WorkflowOperationResult start(final WorkflowInstance workflowInstance, JobContext context)
92 throws WorkflowOperationException {
93
94
95 WorkflowOperationInstance currentOperation = workflowInstance.getCurrentOperation();
96 Configuration config = new Configuration(currentOperation);
97
98 MediaPackage workflowMP = workflowInstance.getMediaPackage();
99 MediaPackage mp = workflowMP;
100
101
102
103 if (config.mpFromSearch()) {
104 logger.info("Getting media package from search service");
105 try {
106 mp = searchService.get(mp.getIdentifier().toString());
107 } catch (NotFoundException | UnauthorizedException e) {
108 throw new WorkflowOperationException("could not get media package " + mp + " from search service.");
109 }
110 }
111
112 logger.info("Submitting {} ({}) as {} to {}",
113 mp.getTitle(), mp.getIdentifier(), config.getFormat().name(), config.getUrl());
114
115 try {
116
117 String mpStr;
118 if (config.getFormat() == Configuration.Format.JSON) {
119 mpStr = MediaPackageParser.getAsJSON(mp);
120 } else {
121 mpStr = MediaPackageParser.getAsXml(mp);
122 }
123
124
125 if (config.debug()) {
126 logger.info(mpStr);
127 }
128
129
130 List<NameValuePair> data = new ArrayList<>();
131 data.add(new BasicNameValuePair("mediapackage", mpStr));
132 data.addAll(config.getAdditionalFields());
133
134
135 HttpPost post = new HttpPost(config.getUrl());
136 post.setEntity(new UrlEncodedFormEntity(data, config.getEncoding()));
137
138
139 HttpClientBuilder clientBuilder = HttpClientBuilder.create();
140
141
142 if (config.authenticate()) {
143 URL targetUrl = config.getUrl().toURL();
144 CredentialsProvider provider = new BasicCredentialsProvider();
145 provider.setCredentials(
146 new AuthScope(targetUrl.getHost(), targetUrl.getPort()),
147 config.getCredentials());
148 clientBuilder.setDefaultCredentialsProvider(provider);
149 }
150 CloseableHttpClient client = clientBuilder.build();
151
152 HttpResponse response = client.execute(post);
153
154
155 int status = response.getStatusLine().getStatusCode();
156 if ((status >= 200) && (status < 300)) {
157 if (config.debug()) {
158 logger.info("Successfully submitted '{}' ({}) to {}: {}", mp.getTitle(), mp.getIdentifier(),
159 config.getUrl(), status);
160 }
161 } else if (status == 418) {
162 logger.warn("Submitted '{}' ({}) to {}: The target claims to be a teapot. "
163 + "The Reason for this is probably an insane developer. Go and help that person!",
164 mp.getTitle(), mp.getIdentifier(), config.getUrl());
165 } else {
166 throw new WorkflowOperationException("Failed to submit \"" + mp.getTitle()
167 + "\" (" + mp.getIdentifier().toString() + "), " + config.getUrl().toString()
168 + " answered with: " + Integer.toString(status));
169 }
170 } catch (Exception e) {
171 if (e instanceof WorkflowOperationException) {
172 throw (WorkflowOperationException) e;
173 } else {
174 throw new WorkflowOperationException(e);
175 }
176 }
177 return createResult(workflowMP, Action.CONTINUE);
178 }
179
180
181 private static class Configuration {
182
183 public enum Format {
184 XML, JSON
185 }
186
187
188 public static final String PROPERTY_URL = "url";
189 public static final String PROPERTY_FORMAT = "format";
190 public static final String PROPERTY_ENCODING = "encoding";
191 public static final String PROPERTY_AUTH = "auth.enabled";
192 public static final String PROPERTY_AUTHUSER = "auth.username";
193 public static final String PROPERTY_AUTHPASSWD = "auth.password";
194 public static final String PROPERTY_DEBUG = "debug";
195 public static final String PROPERTY_MEDIAPACKAGE_TYPE = "mediapackage.type";
196
197
198 private URI url;
199 private Format format = Format.XML;
200 private String encoding = "UTF-8";
201 private boolean authenticate = false;
202 private UsernamePasswordCredentials credentials = null;
203 private List<NameValuePair> additionalFields = new ArrayList<NameValuePair>();
204 private boolean debug = false;
205 private boolean mpFromSearch = true;
206
207 Configuration(WorkflowOperationInstance operation) throws WorkflowOperationException {
208 try {
209 Set<String> keys = operation.getConfigurationKeys();
210
211
212 if (keys.contains(PROPERTY_URL)) {
213 url = new URI(operation.getConfiguration(PROPERTY_URL));
214 } else {
215 throw new IllegalArgumentException("No target URL provided.");
216 }
217
218
219 if (keys.contains(PROPERTY_FORMAT)) {
220 format = Format.valueOf(operation.getConfiguration(PROPERTY_FORMAT).toUpperCase());
221 }
222
223
224 if (keys.contains(PROPERTY_ENCODING)) {
225 encoding = operation.getConfiguration(PROPERTY_ENCODING);
226 }
227
228
229 if (keys.contains(PROPERTY_AUTH)) {
230 String auth = operation.getConfiguration(PROPERTY_AUTH).toUpperCase();
231 if (!("NO").equals(auth) && !("FALSE").equals(auth)) {
232 String username = operation.getConfiguration(PROPERTY_AUTHUSER);
233 String password = operation.getConfiguration(PROPERTY_AUTHPASSWD);
234 if (username == null || password == null) {
235 throw new WorkflowOperationException("Username and Password must be provided for authentication!");
236 }
237 credentials = new UsernamePasswordCredentials(username, password);
238 authenticate = true;
239 }
240 }
241
242
243 if (keys.contains(PROPERTY_DEBUG)) {
244 String debugstr = operation.getConfiguration(PROPERTY_DEBUG).trim().toUpperCase();
245 debug = "YES".equals(debugstr) || "TRUE".equals(debugstr);
246 }
247
248
249 if (keys.contains(PROPERTY_MEDIAPACKAGE_TYPE)) {
250 String cfgval = operation.getConfiguration(PROPERTY_MEDIAPACKAGE_TYPE).trim().toUpperCase();
251 mpFromSearch = "SEARCH".equals(cfgval);
252 }
253
254
255 for (String key : operation.getConfigurationKeys()) {
256 if (key.startsWith("+")) {
257 String value = operation.getConfiguration(key);
258 additionalFields.add(new BasicNameValuePair(key.substring(1), value));
259 }
260 }
261 } catch (Exception e) {
262 throw new WorkflowOperationException("Faild to configure operation instance.", e);
263 }
264 }
265
266 public URI getUrl() {
267 return url;
268 }
269
270 public Format getFormat() {
271 return format;
272 }
273
274 public String getEncoding() {
275 return encoding;
276 }
277
278 public boolean authenticate() {
279 return authenticate;
280 }
281
282 public UsernamePasswordCredentials getCredentials() {
283 return credentials;
284 }
285
286 public List<NameValuePair> getAdditionalFields() {
287 return additionalFields;
288 }
289
290 public boolean debug() {
291 return debug;
292 }
293
294 public boolean mpFromSearch() {
295 return mpFromSearch;
296 }
297 }
298
299
300 }