1 /**
2 * Copyright 2005-2008 the original author or authors.
3 *
4 * Licensed under the Gnu General Pubic License, Version 2.0 (the
5 * "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/gpl-license.php
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the Gnu General Public License for more details.
14 */
15 package org.figure8.join.services.remoting.xmlrpc;
16
17 import org.figure8.join.core.DuplicateEntityException;
18 import org.figure8.join.core.InvalidParameterException;
19 import org.figure8.join.services.remoting.ArtifactService;
20 import org.figure8.join.services.remoting.ResourceService;
21 import org.figure8.join.services.remoting.ProcessControlService;
22 import org.figure8.join.services.remoting.InvalidSessionException;
23 import org.figure8.join.services.remoting.beans.RemoteRelease;
24 import org.figure8.join.services.remoting.beans.RemoteComponent;
25 import org.figure8.join.services.remoting.beans.RemoteDeliverable;
26 import org.figure8.join.services.remoting.beans.RemoteDeliverableType;
27 import org.figure8.join.services.remoting.beans.RemoteResourceVersion;
28 import org.figure8.join.services.security.InvalidLoginException;
29 import org.figure8.join.util.LogUtil;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.beanutils.BeanUtils;
33
34 import java.io.File;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.rmi.RemoteException;
38 import java.util.List;
39 import java.util.Vector;
40 import java.util.Hashtable;
41 /**
42 * This is the default implementation of {@see XmlRcpHandler}
43 * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
44 * @version $Revision: 1.3 $
45 */
46 public class DefaultXmlRpcHandler implements XmlRpcHandler{
47
48
49
50 /** Get a commons logger. */
51 private static final Log log = LogUtil.getLog(DefaultXmlRpcHandler.class);
52
53
54
55
56 /** The ArtifactService implementation to dispatch requests to */
57 private ArtifactService artifactService = null;
58 /** The ResourceService implementation to dispatch requests to */
59 private ResourceService resourceService = null;
60 /** The ProcessControlService implementation to dispatch requests to */
61 private ProcessControlService controlService = null;
62
63
64
65
66 /** Creates a new instance of DefaultXmlRpcHandler */
67 public DefaultXmlRpcHandler(){
68 }
69
70
71
72
73 /** @param service The ArtifactService implementation to use */
74 public void setArtifactService(ArtifactService service){
75 this.artifactService = service;
76 }
77 /** @param service The ResourceService implementation to use */
78 public void setResourceService(ResourceService service){
79 this.resourceService = service;
80 }
81 /** @param service The ProcessControlService implementation to use */
82 public void setProcessControlService(ProcessControlService service){
83 this.controlService = service;
84 }
85
86
87
88
89 /**
90 * Authenticates a remote user onto Join application. In case of successfull
91 * authentication, this method returns a token that should be used later as
92 * a parameter for calling services required authorization.
93 * @param username Login of user for authentication
94 * @param password Password of corresponding user
95 * @return An authentication token to reuse later during service call
96 * @throws InvalidLoginException if user cannot be logged
97 * @throws RemoteException if an exception occurs during the remote conversation
98 */
99 public String login(String username, String password) throws InvalidLoginException, RemoteException{
100 return controlService.login(username, password);
101 }
102
103 /**
104 * Logs out from Join application the user corresponding to the supplied token.
105 * @param token An authentication token obtained with <code>login()</code> method.
106 * @throws RemoteException if an exception occurs during the remote conversation
107 * @return Wether this operation is successfull
108 */
109 public boolean logout(String token) throws RemoteException{
110 controlService.logout(token);
111 return true;
112 }
113
114 /**
115 * Retrieve all the releases managed within the project.
116 * @param token Authentication token provided earlier by login() method
117 * @return An array representing RemoteRelease objects
118 * @throws InvalidSessionException if user has no valid session on server-side
119 * @throws RemoteException if an exception occurs during the remote conversation
120 */
121 public List getReleases(String token) throws InvalidSessionException, RemoteException{
122 return makeList(artifactService.getReleases(token));
123 }
124
125 /**
126 * Retrieve all the deliverable categories (or types) managed within the project.
127 * @param token Authentication token provided earlier by login() method
128 * @return An array representing RemoteDeliverableType objects
129 * @throws InvalidSessionException if user has no valid session on server-side
130 * @throws RemoteException if an exception occurs during the remote conversation
131 */
132 public List getDeliverableTypes(String token) throws InvalidSessionException, RemoteException{
133 return makeList(artifactService.getDeliverableTypes(token));
134 }
135
136 /**
137 * Get the deliverable categories where authenticated user can supply deliverables for.
138 * @param token Authentication token provided earlier by login() method
139 * @return An array representing RemoteDeliverableType objects for deliverables supplying
140 * @throws InvalidSessionException if user has no valid session on server-side
141 * @throws RemoteException if an exception occurs during the remote conversation
142 */
143 public List getDeliverableTypesForDelivery(String token) throws InvalidSessionException, RemoteException{
144 return makeList(artifactService.getDeliverableTypesForDelivery(token));
145 }
146
147 /**
148 * Supply a new deliverable using the remote wrapper. This wrapper must contains
149 * release and type informations. This delivery may include a File object as deliverable
150 * content if deliverables from this type are not retrieved using direct VCS or SCM connection.
151 * @param token Authentication token provided earlier by login() method
152 * @param releaseName The unique name of Release to supply a deliverable for
153 * @param typeKey The unique key of deliverable type this delivery will be bound to
154 * @param versionInfo The version info (or VCS tag info) for this delivery
155 * @param comments The comments on deliverable creation
156 * @param content The deliverable content if not VCS deliverable
157 * @throws InvalidParameterException if deliverable is already registered or parameter is missing
158 * @throws DuplicateEntityException if another deliverable with same key already exists
159 * @throws InvalidSessionException if user has no valid session on server-side
160 * @throws RemoteException if an exception occurs during the remote conversation
161 * @return Wether this operation is successfull
162 */
163 public boolean supplyDeliverable(String token, String releaseName, String typeKey,
164 String versionInfo, String comments, byte[] content)
165 throws InvalidParameterException, DuplicateEntityException, InvalidSessionException, RemoteException{
166
167
168 RemoteRelease release = null;
169 RemoteRelease[] releases = artifactService.getReleases(token);
170 for (int i=0; i<releases.length; i++)
171 if (releases[i].getName().equals(releaseName))
172 release = releases[i];
173
174 RemoteDeliverableType type = null;
175 RemoteDeliverableType[] types = artifactService.getDeliverableTypes(token);
176 for (int i=0; i<types.length; i++)
177 if (types[i].getKey().equals(typeKey))
178 type = types[i];
179
180 RemoteDeliverable deliverable = new RemoteDeliverable(versionInfo, comments, release, type);
181
182
183 File contentFile = null;
184 if (content != null && content.length > 0){
185 try{
186 contentFile = File.createTempFile("join-xmlrpc", ".tmp");
187 contentFile.deleteOnExit();
188 new FileOutputStream(contentFile).write(content);
189 }
190 catch (IOException ioe){
191
192 log.error("Exception occurs while writing delivery content on temporary file");
193 log.error("Here's the errors message: " + ioe.getMessage());
194 throw new RemoteException("IOException while writinn delivery content to file. Delivery is cancelled.");
195 }
196 }
197
198 artifactService.supplyDeliverable(token, deliverable, contentFile);
199 return true;
200 }
201
202 /**
203 * Bind a component to a given assembly.
204 * @param token Authentication token provided earlier by login() method
205 * @param typeKey The unique key of component type this component will be bound to
206 * @param versionInfo The version info for this component
207 * @param size The size in bytes of this component
208 * @param assemblyKey The key of assembly to bind component to
209 * @throws InvalidParameterException if parameter is missing
210 * @throws InvalidSessionException if user has no valid session on server-side
211 * @throws RemoteException if an exception occurs during the remote conversation
212 * @return Wether this operation is successfull
213 */
214 public boolean bindComponent(String token, String typeKey, String versionInfo,
215 long size, String assemblyKey)
216 throws InvalidParameterException, InvalidSessionException, RemoteException{
217
218 RemoteComponent component = new RemoteComponent(versionInfo, size, typeKey);
219 artifactService.bindComponent(token, component, assemblyKey);
220 return true;
221 }
222
223 /**
224 * Set the specified status to a specified <code>Build</code>.
225 * @param token Authentication token provided earlier by login() method
226 * @param statusKey Unique key idenitifer of the status to set to build
227 * @param buildKey The unique key idenitifer of Build to update status
228 * @throws InvalidSessionException if user has no valid session on server-side
229 * @throws RemoteException if an exception occurs during the remote conversation
230 * @return Wether this operation is successfull
231 */
232 public boolean setBuildStatus(String token, String statusKey, String buildKey)
233 throws InvalidSessionException, RemoteException{
234 controlService.setBuildStatus(token, statusKey, buildKey);
235 return true;
236 }
237
238 /**
239 * Set the specified status to a specified <code>Assembly</code>.
240 * @param token Authentication token provided earlier by login() method
241 * @param statusKey Unique key idenitifer of the status to set to assembly
242 * @param assemblyKey The unique key idenitifer of Assembly to update status
243 * @throws InvalidSessionException if user has no valid session on server-side
244 * @throws RemoteException if an exception occurs during the remote conversation
245 * @return Wether this operation is successfull
246 */
247 public boolean setAssemblyStatus(String token, String statusKey, String assemblyKey)
248 throws InvalidSessionException, RemoteException{
249 controlService.setAssemblyStatus(token, statusKey, assemblyKey);
250 return true;
251 }
252
253 /**
254 * Set the specified status to a specified <code>Deployment</code>.
255 * @param token Authentication token provided earlier by login() method
256 * @param statusKey Unique key idenitifer of the status to set to deployment
257 * @param deploymentId The unique identifier of Deployment to retrieve status for
258 * @throws InvalidSessionException if user has no valid session on server-side
259 * @throws NumberFormatException if deploymentId does not represent a long
260 * @throws RemoteException if an exception occurs during the remote conversation
261 * @return Wether this operation is successfull
262 */
263 public boolean setDeploymentStatus(String token, String statusKey, String deploymentId)
264 throws InvalidSessionException, NumberFormatException, RemoteException{
265
266 long deploymentIdL = Long.valueOf(deploymentId).longValue();
267 controlService.setDeploymentStatus(token, statusKey, deploymentIdL);
268 return true;
269 }
270
271 /**
272 * Create a new resource version for a specified resource type. This operation
273 * reqires a valid authentication done first and the permissions corresponding
274 * to Joiner security role.
275 * @param token Authentication token provided earlier by login() method
276 * @param versionName The name of the resource version to create
277 * @param versionDescription The description of resource version to create
278 * @param resourceTypeKey The key of resource type this version applies to
279 * @throws InvalidParameterException if parameter is missing
280 * @throws DuplicateEntityException if another version with same name already exists
281 * @throws InvalidSessionException if user has no valid session on server-side
282 * @throws RemoteException if an exception occurs during the remote conversation
283 * @return Wether this operation is successfull
284 */
285 public boolean createResourceVersion(String token, String versionName,
286 String versionDescription, String resourceTypeKey)
287 throws InvalidParameterException, DuplicateEntityException, InvalidSessionException, RemoteException{
288
289 RemoteResourceVersion version = new RemoteResourceVersion(versionName, versionDescription, resourceTypeKey);
290 resourceService.createResourceVersion(token, version);
291 return true;
292 }
293
294 /**
295 * Update an existing resource with an existing reosurce version. Resource and
296 * version are specified using their names that should be unique within Join system.
297 * This operations requires a valid authentication done first and the persmissions
298 * corresponding to Joiner security role.
299 * @param token Authentication token provided earlier by login() method
300 * @param resourceName The unique name of the resource to update
301 * @param versionName The unique name of the version to update resource with
302 * @throws InvalidParameterException if one of supplied parameters is invalid
303 * @throws InvalidSessionException if user has no valid session on server-side
304 * @throws RemoteException if an exception occurs during the remote conversation
305 * @return Wether this operation is successfull
306 */
307 public boolean updateResource(String token, String resourceName, String versionName)
308 throws InvalidParameterException, InvalidSessionException, RemoteException{
309 resourceService.updateResource(token, resourceName, versionName);
310 return true;
311 }
312
313
314
315
316 /**
317 * Make a Xml-Rpc array of given beans
318 * @return A list that will be serialized into an {@code <array>}
319 */
320 private List makeList(Object[] beans){
321
322 Vector result = new Vector();
323
324 for (int i=0; i<beans.length; i++){
325 if (beans[i] instanceof String)
326 result.add(beans[i]);
327 else
328 result.add(makeStructure(beans[i]));
329 }
330 return result;
331 }
332
333 /**
334 * Make a Xml-Rpc structure of a given bean
335 * @return A Hashtable that will be serialized into a {@code <struct>}
336 */
337 private Hashtable makeStructure(Object bean){
338 try{
339
340 return new Hashtable(BeanUtils.describe(bean)){
341 /**
342 * Do not throw NullPointerException if key or value is null.
343 * Do not store the 'class' property of the given bean.
344 */
345 public synchronized Object put(Object key, Object value){
346 if (key == null || value == null || "class".equals(key))
347 return null;
348 else
349 return super.put(key, value);
350 }
351 };
352 }
353 catch (Exception e){
354
355 log.warn("Exception occurs while making structure for bean: " + bean);
356 return null;
357 }
358 }
359 }