Status: | Merged |
---|---|
Merged at revision: | 339 |
Proposed branch: | lp:~mgiuca/mugle/gamefiles |
Merge into: | lp:mugle |
Diff against target: |
1076 lines (+688/-96) 15 files modified
doc/platform/urls.rst (+3/-2) src/META-INF/mime.types (+35/-0) src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java (+54/-0) src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java (+0/-1) src/au/edu/unimelb/csse/mugle/server/GameFileServer.java (+69/-13) src/au/edu/unimelb/csse/mugle/server/UploadService.java (+197/-0) src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java (+104/-0) src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java (+55/-56) src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java (+89/-4) src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java (+14/-9) src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java (+19/-9) src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java (+15/-0) src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java (+7/-2) src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java (+5/-0) war/WEB-INF/web.xml (+22/-0) |
To merge this branch: | bzr merge lp:~mgiuca/mugle/gamefiles |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MUGLE Developers | Pending | ||
Review via email: mp+61896@code.launchpad.net |
Commit message
Description of the change
If anyone is still up, can you have a look at this diff. It introduces game file uploading. I just want to check if I am doing the database access right -- particularly with the definition of the GameFileContents class, and the way in which GameFileData and GameFileContents objects are created and looked up.
If not, I'll just commit it.
Note that I'm not quite ready to commit; I need to add MIME type detection and game file serving, but that doesn't need serious review.
- 324. By Matt Giuca
-
Merge from trunk.
- 325. By Matt Giuca
-
UploadService: As Scott recommends, open a new PersistenceManager for each file.
- 326. By Matt Giuca
-
GameFileData: Now correctly sets the MIME type of a file based on its extension.
Added src/META-INF/mime. types, a table mapping file extensions to MIME types (manually created). - 327. By Matt Giuca
-
web.xml: Set up URL handler for GameFileServer.
Unfortunately, had to change URL pattern to /+games/* since I can't match /*/*/+play in web.xml, as originally planned. - 328. By Matt Giuca
-
GameFileServer: Started handling URL.
- 329. By Matt Giuca
-
GameFileServer: Clean up debugging output.
- 330. By Matt Giuca
-
GameVersionGetter: Fix getGameVersion by Key double-closing the persistence manager.
- 331. By Matt Giuca
-
GameVersionGetter: More useful methods.
- 332. By Matt Giuca
-
GameGetter: getGameByDevTeam now actually takes a game name.
What ... was it doing before? It seemed to be expecting that each devteam had only one game, and didn't check the name. - 333. By Matt Giuca
-
GameFileServer: Now gets the gameversion associated with the URL, and throws errors if they are missing.
- 334. By Matt Giuca
-
GameFileGetter: More methods.
- 335. By Matt Giuca
-
Fix GameFileGetter error messages.
- 336. By Matt Giuca
-
GameFileGetter: Can now get GameFileContents from a GameFile.
Added new exception type GameFileContentsNotExists. - 337. By Matt Giuca
-
UploadService now uses GameFileGetter.
getGameFileCont ents. - 338. By Matt Giuca
-
GameFileServer now gets the game file and contents specified.
- 339. By Matt Giuca
-
GameFileContents: getContents now works.
- 340. By Matt Giuca
-
GameFileServer: Prints the MIME type and contents to the debugging output.
- 341. By Matt Giuca
-
GameFileServer: Replaced debug output with actual file service.
Now serves the raw bytes of the file with the appropriate MIME type.
Looks good ... up until around 0x200 bytes where it becomes all 0s. - 342. By Matt Giuca
-
GameFileServer: Now sets Content-Length for better browser handling.
- 343. By Matt Giuca
-
GameFileContents: Fix bug where unzipping files beyond a certain size would truncate the file with null bytes.
It needs to read in a loop, because stream.read won't necessarily return all the bytes at once. - 344. By Matt Giuca
-
UploadService: Fix bug where uploading a new file over an old one would not replace the contents.
For some reason you have to call pm.makePersistent on contents. I thought you didn't need to if the object already existed?
(It only is for this object; the other ones you don't need to call makePersistent to make a change. Strange.) - 345. By Matt Giuca
-
UploadService: Change result so it's no longer debug text.
It shows the list of filenames -- it'll do for now as a user interface. - 346. By Matt Giuca
-
UploadService: Remove the first path segment from the filename, so the top-level directory of the zip file is ignored.
- 347. By Matt Giuca
-
GameEditBuilder: Added instructions for uploading a zip file.
Preview Diff
1 | === modified file 'doc/platform/urls.rst' |
2 | --- doc/platform/urls.rst 2011-05-22 08:01:47 +0000 |
3 | +++ doc/platform/urls.rst 2011-05-22 18:34:23 +0000 |
4 | @@ -82,13 +82,14 @@ |
5 | / Alias for /Mugle.html |
6 | /Mugle.html The main web page |
7 | /mugle/ Contains static JavaScript and Ajax URLs |
8 | + /upload Game upload target |
9 | /img/ Contains static images |
10 | /favicon.ico |
11 | /gwt-override.css |
12 | /Mugle.css |
13 | /MugleIE6.css |
14 | - /<promoted-game>/+play/ Static game files for game |
15 | - /<team>/<game>/+play/ Static game files for game |
16 | + /+games/<promoted-game>/ Static game files for game |
17 | + /+games/<team>/<game>/ Static game files for game |
18 | |
19 | Client URLs |
20 | ----------- |
21 | |
22 | === added file 'src/META-INF/mime.types' |
23 | --- src/META-INF/mime.types 1970-01-01 00:00:00 +0000 |
24 | +++ src/META-INF/mime.types 2011-05-22 18:34:23 +0000 |
25 | @@ -0,0 +1,35 @@ |
26 | +# MUGLE MIME types mapping |
27 | +# Used to set the MIME type of files being uploaded |
28 | +# This list should be just about all the file types students will need. |
29 | +# the format is <mime type> <space separated file extensions> |
30 | + |
31 | +# Application |
32 | +application/json json |
33 | +application/javascript js |
34 | +application/pdf pdf |
35 | +application/xhtml+xml xhtml |
36 | +application/zip zip |
37 | +application/x-gzip gz gzip |
38 | + |
39 | +# Text |
40 | +text/plain txt text |
41 | +text/html htm html |
42 | +text/css css |
43 | +text/xml xml |
44 | + |
45 | +# Image |
46 | +image/gif gif |
47 | +image/jpeg jpg jpeg |
48 | +image/png png |
49 | +image/svg svg |
50 | +image/webp webp |
51 | + |
52 | +# Audio |
53 | +audio/mpeg mp3 |
54 | +audio/ogg ogg |
55 | +audio/vnd.wave wav |
56 | + |
57 | +# Video |
58 | +video/mpeg mpeg |
59 | +video/mp4 mp4 |
60 | +video/webm webm |
61 | |
62 | === modified file 'src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java' |
63 | --- src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java 2011-05-22 13:33:19 +0000 |
64 | +++ src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java 2011-05-22 18:34:23 +0000 |
65 | @@ -16,6 +16,10 @@ |
66 | import com.google.gwt.user.client.rpc.AsyncCallback; |
67 | import com.google.gwt.user.client.ui.*; |
68 | import com.google.gwt.user.client.ui.HTMLTable.RowFormatter; |
69 | +import com.google.gwt.user.client.ui.FormPanel; |
70 | +import com.google.gwt.user.client.ui.FileUpload; |
71 | + |
72 | +import com.google.gwt.user.client.Window; |
73 | |
74 | /** |
75 | * Loads the view for editing game information. |
76 | @@ -105,6 +109,7 @@ |
77 | panel.add(warning); |
78 | panel.add(new Label("Badges")); |
79 | panel.add(assembleAchievementsTable()); |
80 | + panel.add(assembleUploadBox(gameVersion)); |
81 | |
82 | // Assemble namePanel |
83 | namePanel.add(new Label("Name: ")); |
84 | @@ -452,6 +457,55 @@ |
85 | } |
86 | }); |
87 | } |
88 | + |
89 | + /** |
90 | + * Creates a widget allowing the user to upload a new game in a zip file. |
91 | + * @param version Game version to upload over the top of. |
92 | + */ |
93 | + private Widget assembleUploadBox(GameVersion version) { |
94 | + final FormPanel form = new FormPanel(); |
95 | + form.setAction("/mugle/upload?gameversion=" |
96 | + + Long.toString(version.getPrimaryKey())); |
97 | + |
98 | + // File upload widget requires multipart/form-data encoding, and POST |
99 | + // method. |
100 | + form.setEncoding(FormPanel.ENCODING_MULTIPART); |
101 | + form.setMethod(FormPanel.METHOD_POST); |
102 | + |
103 | + VerticalPanel vpanel = new VerticalPanel(); |
104 | + form.setWidget(vpanel); |
105 | + vpanel.add(new Label("Upload the game code. Instructions: Compile " |
106 | + + "your GWT project and then zip up the 'war' directory. Your zip " |
107 | + + "file must have a single top-level directory. Upload the zip file " |
108 | + + "here. This will replace any existing versions of the game.")); |
109 | + |
110 | + HorizontalPanel hpanel = new HorizontalPanel(); |
111 | + vpanel.add(hpanel); |
112 | + |
113 | + // Create a FileUpload widget |
114 | + FileUpload upload = new FileUpload(); |
115 | + upload.setName("game_upload"); |
116 | + hpanel.add(upload); |
117 | + |
118 | + // Add a 'submit' button |
119 | + hpanel.add(new Button("Upload", new ClickListener() { |
120 | + public void onClick(Widget sender) { |
121 | + form.submit(); |
122 | + } |
123 | + })); |
124 | + |
125 | + |
126 | + // Handle the response from the server after upload |
127 | + form.addFormHandler(new FormHandler() { |
128 | + public void onSubmit(FormSubmitEvent event) {} |
129 | + |
130 | + public void onSubmitComplete(FormSubmitCompleteEvent event) { |
131 | + Window.alert(event.getResults()); |
132 | + } |
133 | + }); |
134 | + |
135 | + return form; |
136 | + } |
137 | |
138 | /** |
139 | * Constructs the Navigation bar down the bottom (with delete button) |
140 | |
141 | === modified file 'src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java' |
142 | --- src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java 2011-05-22 13:10:42 +0000 |
143 | +++ src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java 2011-05-22 18:34:23 +0000 |
144 | @@ -100,7 +100,6 @@ |
145 | |
146 | gamefiles[0].setPath("index.html"); |
147 | gamefiles[0].setMimeType("text/html"); |
148 | - gamefiles[0].setBlobKey(null); // XXX |
149 | gamefiles[0].setGameVersionKey(gameversions[0].getPrimaryKey()); |
150 | |
151 | GameFileServiceImpl gfs = new GameFileServiceImpl(); |
152 | |
153 | === modified file 'src/au/edu/unimelb/csse/mugle/server/GameFileServer.java' |
154 | --- src/au/edu/unimelb/csse/mugle/server/GameFileServer.java 2011-05-20 06:35:39 +0000 |
155 | +++ src/au/edu/unimelb/csse/mugle/server/GameFileServer.java 2011-05-22 18:34:23 +0000 |
156 | @@ -16,37 +16,92 @@ |
157 | */ |
158 | package au.edu.unimelb.csse.mugle.server; |
159 | |
160 | +import java.io.OutputStream; |
161 | import java.io.IOException; |
162 | |
163 | import javax.servlet.http.HttpServlet; |
164 | import javax.servlet.http.HttpServletRequest; |
165 | import javax.servlet.http.HttpServletResponse; |
166 | |
167 | +import com.google.appengine.api.datastore.Key; |
168 | + |
169 | +import au.edu.unimelb.csse.mugle.server.model.GameData; |
170 | +import au.edu.unimelb.csse.mugle.server.model.GameGetter; |
171 | +import au.edu.unimelb.csse.mugle.server.model.GameVersionData; |
172 | +import au.edu.unimelb.csse.mugle.server.model.GameVersionGetter; |
173 | import au.edu.unimelb.csse.mugle.server.model.GameFileData; |
174 | import au.edu.unimelb.csse.mugle.server.model.GameFileGetter; |
175 | +import au.edu.unimelb.csse.mugle.server.model.GameFileContents; |
176 | +import au.edu.unimelb.csse.mugle.shared.platform.exceptions.DevTeamNotExists; |
177 | import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists; |
178 | +import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileContentsNotExists; |
179 | import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists; |
180 | import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists; |
181 | |
182 | -import com.google.appengine.api.blobstore.BlobKey; |
183 | -import com.google.appengine.api.blobstore.BlobstoreService; |
184 | -import com.google.appengine.api.blobstore.BlobstoreServiceFactory; |
185 | - |
186 | @SuppressWarnings("serial") |
187 | public class GameFileServer extends HttpServlet { |
188 | |
189 | public void doGet(HttpServletRequest req, HttpServletResponse res) |
190 | throws IOException { |
191 | - BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService(); |
192 | - |
193 | - try { |
194 | - BlobKey blobKey = getBlobKey(req); |
195 | - blobstoreService.serve(blobKey, res); |
196 | - } catch (GameFileNotExists e) { |
197 | - //TODO: Construct a page with the error |
198 | - } |
199 | + String path = req.getPathInfo(); |
200 | + if (path.length() > 0 && path.charAt(0) == '/') |
201 | + path = path.substring(1); |
202 | + String[] pathSegments = path.split("/", 3); |
203 | + // XXX Assumes a devteam/game, not a promoted game |
204 | + if (pathSegments.length != 3) |
205 | + { |
206 | + res.sendError(404, "Not found"); |
207 | + return; |
208 | + } |
209 | + String devteamName = pathSegments[0]; |
210 | + String gameName = pathSegments[1]; |
211 | + String filePath = pathSegments[2]; |
212 | + |
213 | + GameFileData file; |
214 | + GameFileContents contents; |
215 | + try |
216 | + { |
217 | + GameData game = GameGetter.getGameByDevTeam(devteamName,gameName); |
218 | + Key versionKey = game.getActiveVersion(); |
219 | + GameVersionData version = |
220 | + GameVersionGetter.getGameVersion(versionKey); |
221 | + file = GameFileGetter.getGameFile(game, version, filePath); |
222 | + contents = GameFileGetter.getGameFileContents(file); |
223 | + } |
224 | + catch (DevTeamNotExists e) |
225 | + { |
226 | + res.sendError(404, e.toString()); |
227 | + return; |
228 | + } |
229 | + catch (GameNotExists e) |
230 | + { |
231 | + res.sendError(404, e.toString()); |
232 | + return; |
233 | + } |
234 | + catch (GameVersionNotExists e) |
235 | + { |
236 | + res.sendError(404, e.toString()); |
237 | + return; |
238 | + } |
239 | + catch (GameFileNotExists e) |
240 | + { |
241 | + res.sendError(404, e.toString()); |
242 | + return; |
243 | + } |
244 | + catch (GameFileContentsNotExists e) |
245 | + { |
246 | + res.sendError(404, e.toString()); |
247 | + return; |
248 | + } |
249 | + |
250 | + // Serve the file on the HTTP response |
251 | + res.setContentType(file.getMimeType()); |
252 | + byte[] bytes = contents.getContents(); |
253 | + res.setContentLength(bytes.length); |
254 | + res.getOutputStream().write(bytes); |
255 | } |
256 | |
257 | + /* |
258 | private BlobKey getBlobKey(HttpServletRequest req) |
259 | throws GameFileNotExists { |
260 | |
261 | @@ -57,7 +112,7 @@ |
262 | /* We need to parse the request to get the gameName, gameVersion |
263 | * and path, |
264 | * URLs are in the form /game/gameName/gameVersion/path |
265 | - */ |
266 | + * / |
267 | String toParse = req.getRequestURI().split("/game/")[1]; // gets everything after /game/ in the URI |
268 | String[] splitted = toParse.split("/"); |
269 | gameName = splitted[0]; |
270 | @@ -74,6 +129,7 @@ |
271 | } |
272 | |
273 | } |
274 | + */ |
275 | |
276 | /* |
277 | public void doPost(HttpServletRequest req, HttpServletResponse res) |
278 | |
279 | === added file 'src/au/edu/unimelb/csse/mugle/server/UploadService.java' |
280 | --- src/au/edu/unimelb/csse/mugle/server/UploadService.java 1970-01-01 00:00:00 +0000 |
281 | +++ src/au/edu/unimelb/csse/mugle/server/UploadService.java 2011-05-22 18:34:23 +0000 |
282 | @@ -0,0 +1,197 @@ |
283 | +/* Melbourne University Game-based Learning Environment |
284 | + * Copyright (C) 2011 The University of Melbourne |
285 | + * |
286 | + * This program is free software: you can redistribute it and/or modify |
287 | + * it under the terms of the GNU General Public License as published by |
288 | + * the Free Software Foundation, either version 3 of the License, or |
289 | + * (at your option) any later version. |
290 | + * |
291 | + * This program is distributed in the hope that it will be useful, |
292 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
293 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
294 | + * GNU General Public License for more details. |
295 | + * |
296 | + * You should have received a copy of the GNU General Public License |
297 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
298 | + */ |
299 | +package au.edu.unimelb.csse.mugle.server; |
300 | + |
301 | +import java.io.InputStream; |
302 | +import java.io.PrintWriter; |
303 | +import java.io.IOException; |
304 | +import javax.servlet.http.HttpServlet; |
305 | +import javax.servlet.http.HttpServletRequest; |
306 | +import javax.servlet.http.HttpServletResponse; |
307 | +import javax.jdo.PersistenceManager; |
308 | + |
309 | +import java.util.zip.ZipInputStream; |
310 | +import java.util.zip.ZipEntry; |
311 | +import java.util.zip.ZipException; |
312 | + |
313 | +import org.apache.commons.fileupload.FileItemIterator; |
314 | +import org.apache.commons.fileupload.FileItemStream; |
315 | +import org.apache.commons.fileupload.servlet.ServletFileUpload; |
316 | +import org.apache.commons.fileupload.FileUploadException; |
317 | + |
318 | +import java.util.Vector; |
319 | + |
320 | +import au.edu.unimelb.csse.mugle.server.model.GameFileData; |
321 | +import au.edu.unimelb.csse.mugle.server.model.GameFileContents; |
322 | +import au.edu.unimelb.csse.mugle.server.model.GameFileGetter; |
323 | +import au.edu.unimelb.csse.mugle.server.model.GameVersionData; |
324 | +import au.edu.unimelb.csse.mugle.server.PMF; |
325 | +import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists; |
326 | +import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileContentsNotExists; |
327 | + |
328 | +public class UploadService extends HttpServlet |
329 | +{ |
330 | + public void doPost(HttpServletRequest request, |
331 | + HttpServletResponse response) throws IOException |
332 | + { |
333 | + long gameversionID = Long.parseLong( |
334 | + request.getParameter("gameversion")); |
335 | + GameVersionData gameversion; |
336 | + boolean got_zip = false; |
337 | + int numfiles = 0; |
338 | + Vector<String> filenames = new Vector<String>(); |
339 | + |
340 | + // Read the POST data fields |
341 | + ServletFileUpload upload = new ServletFileUpload(); |
342 | + PersistenceManager pm = PMF.getManager(); |
343 | + try |
344 | + { |
345 | + // Get the GameVersion object |
346 | + gameversion = |
347 | + pm.getObjectById(GameVersionData.class, gameversionID); |
348 | + } |
349 | + finally |
350 | + { |
351 | + pm.close(); |
352 | + } |
353 | + try |
354 | + { |
355 | + // For each field |
356 | + FileItemIterator iter = upload.getItemIterator(request); |
357 | + while (iter.hasNext()) |
358 | + { |
359 | + FileItemStream item = iter.next(); |
360 | + // Only care about the game_upload field |
361 | + if (!item.getFieldName().equals("game_upload")) |
362 | + continue; |
363 | + got_zip = true; |
364 | + |
365 | + // Assume it is a ZIP file. Read each file contained inside. |
366 | + InputStream stream = item.openStream(); |
367 | + ZipInputStream zstream = new ZipInputStream(stream); |
368 | + ZipEntry entry; |
369 | + while ((entry = zstream.getNextEntry()) != null) |
370 | + { |
371 | + if (entry.isDirectory()) |
372 | + { |
373 | + // Ignore directories (since we are not building a |
374 | + // tree; just remembering the path to each file) |
375 | + continue; |
376 | + } |
377 | + String filename = entry.getName(); |
378 | + // Remove the first path segment from the filename (since |
379 | + // ZIP files tend to have a top-level directory, and we |
380 | + // want to explode out of that). |
381 | + String[] filename_split = filename.split("/", 2); |
382 | + if (filename_split.length > 1) |
383 | + filename = filename_split[1]; |
384 | + long size = entry.getSize(); |
385 | + // Create a new GameFile object |
386 | + createGameFile(gameversion, filename, zstream, size); |
387 | + filenames.add(filename); |
388 | + numfiles++; |
389 | + } |
390 | + } |
391 | + } |
392 | + catch (FileUploadException e) |
393 | + { |
394 | + response.sendError(400, e.toString()); |
395 | + return; |
396 | + } |
397 | + catch (ZipException e) |
398 | + { |
399 | + response.sendError(400, e.toString()); |
400 | + return; |
401 | + } |
402 | + if (!got_zip) |
403 | + { |
404 | + response.sendError(400, "Missing POST field game_upload"); |
405 | + return; |
406 | + } |
407 | + |
408 | + // Set HTML content type, even though returning text |
409 | + // The result is shown in an alert, and if it's text, Firefox will add |
410 | + // "<pre>". |
411 | + response.setContentType("text/html"); |
412 | + PrintWriter out = response.getWriter(); |
413 | + out.println("Successfully uploaded a ZIP file with " |
414 | + + numfiles + " files:"); |
415 | + for (String s : filenames) |
416 | + out.println("- " + s); |
417 | + } |
418 | + |
419 | + /** Create a new GameFile and store it in the database, from a part of a |
420 | + * Zip file. |
421 | + * @param gameVersionKey The game version to upload this file to. |
422 | + * @param filePath Path to the file relative to this game version. |
423 | + * @param data Binary stream to read file contents from. |
424 | + * @param dataSize Number of bytes to read from data. |
425 | + */ |
426 | + public static void createGameFile(GameVersionData gameVersion, |
427 | + String filePath, InputStream data, long dataSize) |
428 | + throws IOException |
429 | + { |
430 | + PersistenceManager pm = PMF.getManager(); |
431 | + try |
432 | + { |
433 | + // Check if a file already exists at that path |
434 | + GameFileData file; |
435 | + GameFileContents contents; |
436 | + try |
437 | + { |
438 | + file = GameFileGetter.getGameFile(pm, gameVersion, filePath); |
439 | + } |
440 | + catch (GameFileNotExists e) |
441 | + { |
442 | + // First, store the contents as a blob in the database |
443 | + contents = new GameFileContents(data, dataSize); |
444 | + pm.makePersistent(contents); |
445 | + // Then, create a GameFile which points at that blob |
446 | + file = new GameFileData(gameVersion, filePath, contents); |
447 | + pm.makePersistent(file); |
448 | + return; |
449 | + } |
450 | + // Already exists; just update the file and contents |
451 | + // (Note that if we were uploading a new version, we wouldn't |
452 | + // overwrite the file or contents, but since we're updating an |
453 | + // existing version, we can replace the contents). |
454 | + // XXX Note that if contents are shared across versions this will |
455 | + // clobber ALL versions, so probably we should create a new |
456 | + // content object each time. |
457 | + try |
458 | + { |
459 | + contents = GameFileGetter.getGameFileContents(file); |
460 | + contents.setContents(data, dataSize); |
461 | + pm.makePersistent(contents); |
462 | + } |
463 | + catch (GameFileContentsNotExists e) |
464 | + { |
465 | + contents = new GameFileContents(data, dataSize); |
466 | + pm.makePersistent(contents); |
467 | + file.setContents(contents.getServerKey()); |
468 | + } |
469 | + // Also set the MIME type based on the filename (so the result is |
470 | + // consistent with what would have happened if the file didn't |
471 | + // exist). |
472 | + file.setMimeTypeFromFilename(); |
473 | + } |
474 | + finally |
475 | + { |
476 | + pm.close(); |
477 | + } |
478 | + } |
479 | +} |
480 | |
481 | === added file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java' |
482 | --- src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java 1970-01-01 00:00:00 +0000 |
483 | +++ src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java 2011-05-22 18:34:23 +0000 |
484 | @@ -0,0 +1,104 @@ |
485 | +/* Melbourne University Game-based Learning Environment |
486 | + * Copyright (C) 2011 The University of Melbourne |
487 | + * |
488 | + * This program is free software: you can redistribute it and/or modify |
489 | + * it under the terms of the GNU General Public License as published by |
490 | + * the Free Software Foundation, either version 3 of the License, or |
491 | + * (at your option) any later version. |
492 | + * |
493 | + * This program is distributed in the hope that it will be useful, |
494 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
495 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
496 | + * GNU General Public License for more details. |
497 | + * |
498 | + * You should have received a copy of the GNU General Public License |
499 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
500 | + */ |
501 | + |
502 | +package au.edu.unimelb.csse.mugle.server.model; |
503 | + |
504 | +import java.io.OutputStream; |
505 | +import java.io.InputStream; |
506 | +import java.io.IOException; |
507 | + |
508 | +import javax.jdo.PersistenceManager; |
509 | +import javax.jdo.annotations.IdGeneratorStrategy; |
510 | +import javax.jdo.annotations.PersistenceCapable; |
511 | +import javax.jdo.annotations.Persistent; |
512 | +import javax.jdo.annotations.PrimaryKey; |
513 | + |
514 | +import com.google.appengine.api.datastore.Key; |
515 | +import com.google.appengine.api.datastore.Blob; |
516 | + |
517 | +/** The contents of a GameFile. |
518 | + * This simple class contains only the contents and no other meta-data. It |
519 | + * may be referenced by one or more GameFile objects. |
520 | + */ |
521 | +@PersistenceCapable |
522 | +public class GameFileContents { |
523 | + // Fields |
524 | + |
525 | + @PrimaryKey |
526 | + @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) |
527 | + private Key id; |
528 | + |
529 | + @Persistent |
530 | + private Blob data; |
531 | + |
532 | + /** Create a new file contents blob, from a given input stream. |
533 | + @param size The number of bytes to read from stream. |
534 | + */ |
535 | + public GameFileContents(InputStream stream, long size) |
536 | + throws IOException |
537 | + { |
538 | + this.setContents(stream, size); |
539 | + } |
540 | + |
541 | + // Getters |
542 | + |
543 | + public Key getServerKey() { |
544 | + return this.id; |
545 | + } |
546 | + |
547 | + // Misc methods |
548 | + |
549 | + /** Get the contents of the file as a byte array. |
550 | + */ |
551 | + public byte[] getContents() |
552 | + { |
553 | + return this.data.getBytes(); |
554 | + } |
555 | + |
556 | + /** Write the contents of the file to the OutputStream object. |
557 | + * @param writer Stream object to receive the contents of the file. |
558 | + */ |
559 | + public void getContents(OutputStream writer) |
560 | + throws IOException |
561 | + { |
562 | + byte[] bytes = this.data.getBytes(); |
563 | + writer.write(bytes); |
564 | + } |
565 | + |
566 | + /** Read the contents of an InputStream and store it in the database as |
567 | + * the contents of this file. |
568 | + * @param stream Stream object to read contents of file from. |
569 | + @param size The number of bytes to read from stream. |
570 | + */ |
571 | + public void setContents(InputStream stream, long size) |
572 | + throws IOException |
573 | + { |
574 | + //TODO: check that the DevTeam's space limit hasn't been exceeded. |
575 | + byte[] bytes = new byte[(int)size]; |
576 | + // Read in a loop |
577 | + // (stream.read might read fewer than the requested number of bytes) |
578 | + int offset = 0; |
579 | + int remaining = (int)size; |
580 | + while (remaining > 0) |
581 | + { |
582 | + int bytes_read = stream.read(bytes, offset, remaining); |
583 | + offset += bytes_read; |
584 | + remaining -= bytes_read; |
585 | + } |
586 | + this.data = new Blob(bytes); |
587 | + } |
588 | +} |
589 | |
590 | === modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java' |
591 | --- src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java 2011-05-22 11:06:46 +0000 |
592 | +++ src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java 2011-05-22 18:34:23 +0000 |
593 | @@ -24,9 +24,10 @@ |
594 | import javax.jdo.annotations.PersistenceCapable; |
595 | import javax.jdo.annotations.Persistent; |
596 | import javax.jdo.annotations.PrimaryKey; |
597 | +import javax.activation.MimetypesFileTypeMap; |
598 | |
599 | -import com.google.appengine.api.blobstore.BlobKey; |
600 | import com.google.appengine.api.datastore.Key; |
601 | +import com.google.appengine.api.datastore.Blob; |
602 | |
603 | import au.edu.unimelb.csse.mugle.shared.model.*; |
604 | import au.edu.unimelb.csse.mugle.server.PMF; |
605 | @@ -50,7 +51,7 @@ |
606 | private String mimeType; //mime type of the file |
607 | |
608 | @Persistent |
609 | - private BlobKey blobKey; //key to the actual blob -- may be shared between GameFiles across versions |
610 | + private Key contents; //key to the GameFileContents |
611 | |
612 | @Persistent |
613 | private Key version; //Game version this Gamefile belongs to |
614 | @@ -72,23 +73,43 @@ |
615 | public GameFileData() { |
616 | this.version = null; |
617 | this.setMimeType(null); |
618 | - this.setBlobKey(null); |
619 | this.path = null; |
620 | } |
621 | |
622 | -/** TODO: Pending Blob implementation for files |
623 | - // For creation of the file for the first time |
624 | - public GameFile (String path, GameVersion version, InputStream contents) { |
625 | - this.path = path; |
626 | - this.versions.add(version.getPrimaryKey()); |
627 | - this.setContents(contents); |
628 | - } |
629 | -*/ |
630 | + /** Create a new GameFileData object. |
631 | + * Guesses the file's MIME type based on the file extension. |
632 | + * @param version The game version the file belongs to. |
633 | + * @param path Path to the file relative to this gameversion. |
634 | + * @param contents Blob containing the file's contents. |
635 | + */ |
636 | + public GameFileData(GameVersionData version, String path, |
637 | + GameFileContents contents) |
638 | + { |
639 | + this.version = version.getServerKey(); |
640 | + this.path = path; |
641 | + this.setMimeTypeFromFilename(); |
642 | + this.contents = contents.getServerKey(); |
643 | + } |
644 | + |
645 | + /** Create a new GameFileData object. |
646 | + * @param version The game version the file belongs to. |
647 | + * @param path Path to the file relative to this gameversion. |
648 | + * @param mimeType MIME type of the file. |
649 | + * @param contents Blob containing the file's contents. |
650 | + */ |
651 | + public GameFileData(GameVersionData version, String path, String mimeType, |
652 | + GameFileContents contents) |
653 | + { |
654 | + this.version = version.getServerKey(); |
655 | + this.path = path; |
656 | + this.mimeType = mimeType; |
657 | + this.contents = contents.getServerKey(); |
658 | + } |
659 | |
660 | /* When a file is moved, or renamed - The existing GameFile entry should be deleted, |
661 | * in which case, we may want to initialise with multiple Game versions. |
662 | */ |
663 | -/** TODO: Pending Blob implementation for files |
664 | +/** TODO: Pending multiple versions of games |
665 | public GameFile (String path, Set<String> versions, InputStream contents) { |
666 | this.path = path; |
667 | this.versions = versions; |
668 | @@ -122,18 +143,6 @@ |
669 | return mimeType; |
670 | } |
671 | |
672 | - @GetterUserLevel(privateView=Role.GUEST, publicView=Role.GUEST, mappedTo="setBlobKey", overrideBy="getBlobKeyString") |
673 | - public BlobKey getBlobKey() { |
674 | - return this.blobKey; |
675 | - } |
676 | - |
677 | - public String getBlobKeyString() { |
678 | - if (this.blobKey == null) { |
679 | - return null; |
680 | - } |
681 | - return this.blobKey.getKeyString(); |
682 | - } |
683 | - |
684 | @Override |
685 | @GetterUserLevel(privateView=Role.GUEST, publicView=Role.GUEST, mappedTo="setCreatedUser", overrideBy="keyToCreated") |
686 | public Key getCreatedUser() { |
687 | @@ -209,40 +218,30 @@ |
688 | this.mimeType = mimeType; |
689 | } |
690 | |
691 | - // TODO: do we allow the clients to modify this value |
692 | - //@SetterUserLevel(getter="", mappedBy="") |
693 | - public void setBlobKey(BlobKey blobKey) { |
694 | - this.blobKey = blobKey; |
695 | - } |
696 | - |
697 | // Misc methods |
698 | |
699 | - /** Write the contents of the file to the PrintWriter object. |
700 | - * @param writer Stream object to receive the contents of the file. |
701 | - */ |
702 | -/** TODO: Pending Blob implementation for files |
703 | - public void getContents(PrintWriter writer) { |
704 | - //TODO: retrieve file from database |
705 | - } |
706 | -*/ |
707 | - |
708 | - /** Read the contents of an InputStream and store it in the database as |
709 | - * the contents of this file. |
710 | - * @param stream Stream object to read contents of file from. |
711 | - */ |
712 | -/** TODO: Pending Blob implementation for files |
713 | - public void setContents(InputStream stream) { |
714 | - //TODO: add/create the file in the database |
715 | - //TODO: check that the DevTeam's space limit hasn't been exceeded. |
716 | - } |
717 | -*/ |
718 | - |
719 | - |
720 | -/** TODO: Pending Blob implementation for files |
721 | - public GameFileData(String path, GameVersion version, InputStream contents) { |
722 | - super(path, version, contents); |
723 | - } |
724 | -*/ |
725 | + /** Get the content blob of this file. |
726 | + * @return Key of the GameFileContents object. |
727 | + */ |
728 | + public Key getContents() { |
729 | + return this.contents; |
730 | + } |
731 | + |
732 | + /** Set the content blob of this file. |
733 | + * @param contents Key of the GameFileContents object. |
734 | + */ |
735 | + public void setContents(Key contents) { |
736 | + this.contents = contents; |
737 | + } |
738 | + |
739 | + /** Set the MIME type based on the file extension. |
740 | + */ |
741 | + public void setMimeTypeFromFilename() { |
742 | + String f = this.path.toLowerCase(); |
743 | + // This implicitly looks at the MIME mapping table in |
744 | + // src/META-INF/mime.types. |
745 | + this.mimeType = new MimetypesFileTypeMap().getContentType(this.path); |
746 | + } |
747 | |
748 | @Override |
749 | public Key getServerKey() { |
750 | |
751 | === modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java' |
752 | --- src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java 2011-05-22 11:06:46 +0000 |
753 | +++ src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java 2011-05-22 18:34:23 +0000 |
754 | @@ -27,6 +27,7 @@ |
755 | |
756 | import au.edu.unimelb.csse.mugle.server.PMF; |
757 | import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists; |
758 | +import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileContentsNotExists; |
759 | import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists; |
760 | import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists; |
761 | |
762 | @@ -114,6 +115,27 @@ |
763 | |
764 | /** |
765 | * Gets the GameFile by its path, version and game, for editing object in datastore |
766 | + * @param gameName the name of the game |
767 | + * @param gameVersion the version of the game |
768 | + * @param path the path of the file |
769 | + * @return the GameFile - read only |
770 | + * @throws GameNotExists |
771 | + * @throws GameVersionNotExists |
772 | + * @throws GameFileNotExists |
773 | + */ |
774 | + @SuppressWarnings("unchecked") |
775 | + public static GameFileData getGameFile(GameData game, GameVersionData gameVersion, String path) |
776 | + throws GameFileNotExists { |
777 | + PersistenceManager pm = PMF.getManager(); |
778 | + try { |
779 | + return getGameFile(pm, game, gameVersion, path); |
780 | + } finally { |
781 | + pm.close(); |
782 | + } |
783 | + } |
784 | + |
785 | + /** |
786 | + * Gets the GameFile by its path, version and game, for editing object in datastore |
787 | * PersistenceManager must be handled by the caller |
788 | * @para pm The Persistence Manager |
789 | * @param gameName the name of the game |
790 | @@ -126,14 +148,15 @@ |
791 | */ |
792 | @SuppressWarnings("unchecked") |
793 | public static GameFileData getGameFile(PersistenceManager pm, GameData game, GameVersionData gameVersion, String path) |
794 | - throws GameFileNotExists, GameNotExists, GameVersionNotExists { |
795 | + throws GameFileNotExists { |
796 | |
797 | Query q = pm.newQuery(GameFileData.class, "path == p && version == v"); |
798 | q.declareParameters("String p, com.google.appengine.api.datastore.Key v"); |
799 | List<GameFileData> results = (List<GameFileData>) q.execute(path, gameVersion.getServerKey()); |
800 | |
801 | if (results.isEmpty()) { |
802 | - throw new GameFileNotExists(game.getUrlName(), String.valueOf(gameVersion), path); |
803 | + throw new GameFileNotExists(game.getUrlName(), |
804 | + gameVersion.getDisplayVersion(), path); |
805 | } |
806 | return results.get(0); |
807 | } |
808 | @@ -160,13 +183,41 @@ |
809 | List<GameFileData> results = (List<GameFileData>) q.execute(path, gvd.getServerKey()); |
810 | |
811 | if (results.isEmpty()) { |
812 | - throw new GameFileNotExists(game.getUrlName(), String.valueOf(gameVersion), path); |
813 | + throw new GameFileNotExists(game.getUrlName(), |
814 | + Integer.toString(gameVersion), path); |
815 | } |
816 | |
817 | return results.get(0); |
818 | } |
819 | |
820 | /** |
821 | + * Gets the GameFile by its path and version, for editing object in |
822 | + * datastore. |
823 | + * PersistenceManager must be handled by the caller |
824 | + * @para pm The Persistence Manager |
825 | + * @param gameVersion the version of the game |
826 | + * @param path the path of the file |
827 | + * @return the GameFile - read only |
828 | + * @throws GameFileNotExists |
829 | + */ |
830 | + @SuppressWarnings("unchecked") |
831 | + public static GameFileData getGameFile(PersistenceManager pm, |
832 | + GameVersionData gameVersion, String path) |
833 | + throws GameFileNotExists { |
834 | + |
835 | + Query q = pm.newQuery(GameFileData.class, "path == p && version == v"); |
836 | + q.declareParameters("String p, com.google.appengine.api.datastore.Key v"); |
837 | + List<GameFileData> results = (List<GameFileData>) q.execute(path, |
838 | + gameVersion.getServerKey()); |
839 | + |
840 | + if (results.isEmpty()) { |
841 | + throw new GameFileNotExists(gameVersion.getDisplayVersion(), path); |
842 | + } |
843 | + |
844 | + return results.get(0); |
845 | + } |
846 | + |
847 | + /** |
848 | * Gets the GameFile associated with the given GameFileData primary key. |
849 | * @param pm The PersistenceManager |
850 | * @param key The server-side key (ModelDataClass.getServerKey()) |
851 | @@ -187,5 +238,39 @@ |
852 | } |
853 | |
854 | } |
855 | - |
856 | + |
857 | + /** |
858 | + * Gets the GameFileContents associated with the given GameFile. |
859 | + * @param file The file to retrieve contents for. |
860 | + * @return The GameFileContents object directly off the datastore. |
861 | + * @throws GameFileContentsNotExists |
862 | + */ |
863 | + public static GameFileContents getGameFileContents(GameFileData file) |
864 | + throws GameFileContentsNotExists { |
865 | + PersistenceManager pm = PMF.getManager(); |
866 | + try { |
867 | + return getGameFileContents(pm, file); |
868 | + } finally { |
869 | + pm.close(); |
870 | + } |
871 | + } |
872 | + |
873 | + /** |
874 | + * Gets the GameFileContents associated with the given GameFile. |
875 | + * @param pm The PersistenceManager |
876 | + * @param file The file to retrieve contents for. |
877 | + * @return The GameFileContents object directly off the datastore. |
878 | + * @throws GameFileContentsNotExists |
879 | + */ |
880 | + public static GameFileContents getGameFileContents(PersistenceManager pm, |
881 | + GameFileData file) throws GameFileContentsNotExists { |
882 | + Key key = file.getContents(); |
883 | + GameFileContents result = pm.getObjectById(GameFileContents.class, |
884 | + key); |
885 | + if (result == null) |
886 | + { |
887 | + throw new GameFileContentsNotExists(key.getId()); |
888 | + } |
889 | + return result; |
890 | + } |
891 | } |
892 | |
893 | === modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java' |
894 | --- src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java 2011-05-20 06:35:39 +0000 |
895 | +++ src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java 2011-05-22 18:34:23 +0000 |
896 | @@ -185,15 +185,17 @@ |
897 | |
898 | /** |
899 | * Gets the Game by the name of the DevTeam it belongs to - READ ONLY |
900 | - * @param name name of the DevTeam |
901 | + * @param devTeamName name of the DevTeam |
902 | + * @param gameName name of the Game |
903 | * @return the Game - read only |
904 | * @throws GameNotExists |
905 | * @throws DevTeamNotExists |
906 | */ |
907 | - public static GameData getGameByDevTeam(String name) throws GameNotExists, DevTeamNotExists { |
908 | + public static GameData getGameByDevTeam(String devTeamName, |
909 | + String gameName) throws GameNotExists, DevTeamNotExists { |
910 | PersistenceManager pm = PMF.getManager(); |
911 | try { |
912 | - return getGameByDevTeam(pm, name); |
913 | + return getGameByDevTeam(pm, devTeamName, gameName); |
914 | } finally { |
915 | pm.close(); |
916 | } |
917 | @@ -209,14 +211,17 @@ |
918 | * @throws DevTeamNotExists |
919 | */ |
920 | @SuppressWarnings("unchecked") |
921 | - public static GameData getGameByDevTeam(PersistenceManager pm, String name) throws GameNotExists, DevTeamNotExists{ |
922 | - DevTeamData dtd = DevTeamGetter.getDevTeam(name); |
923 | - Query q = pm.newQuery(GameData.class, "devTeam == d"); |
924 | - q.declareParameters("com.google.appengine.api.datastore.Key d"); |
925 | - List<GameData> results = (List<GameData>) q.execute(dtd.getServerKey()); |
926 | + public static GameData getGameByDevTeam(PersistenceManager pm, |
927 | + String devTeamName, String gameName) |
928 | + throws GameNotExists, DevTeamNotExists{ |
929 | + DevTeamData dtd = DevTeamGetter.getDevTeam(devTeamName); |
930 | + Query q = pm.newQuery(GameData.class, "devTeam == d && urlName == g"); |
931 | + q.declareParameters("com.google.appengine.api.datastore.Key d, String g"); |
932 | + List<GameData> results = (List<GameData>) |
933 | + q.execute(dtd.getServerKey(), gameName); |
934 | |
935 | if(results.isEmpty()) { |
936 | - throw new GameNotExists(name); |
937 | + throw new GameNotExists(devTeamName, gameName); |
938 | } |
939 | |
940 | return results.get(0); |
941 | |
942 | === modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java' |
943 | --- src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java 2011-05-22 07:40:42 +0000 |
944 | +++ src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java 2011-05-22 18:34:23 +0000 |
945 | @@ -155,6 +155,21 @@ |
946 | } |
947 | |
948 | /** |
949 | + * Gets the GameVersion by its Primary Key - READ ONLY |
950 | + * @param primaryKey |
951 | + * @return the GameVersion - read only |
952 | + * @throws GameVersionNotExists |
953 | + */ |
954 | + public static GameVersionData getGameVersion(Key primaryKey) throws GameVersionNotExists { |
955 | + PersistenceManager pm = PMF.getManager(); |
956 | + try { |
957 | + return getGameVersion(pm, primaryKey); |
958 | + } finally { |
959 | + pm.close(); |
960 | + } |
961 | + } |
962 | + |
963 | + /** |
964 | * Gets the GameVersion associated with the given GameVersionData primary key. |
965 | * @param pm The PersistenceManager |
966 | * @param key The server-side key (ModelDataClass.getServerKey()) |
967 | @@ -164,16 +179,11 @@ |
968 | public static GameVersionData getGameVersion(PersistenceManager pm, Key key) |
969 | throws GameVersionNotExists { |
970 | |
971 | - try { |
972 | - GameVersionData result = pm.getObjectById(GameVersionData.class, key); |
973 | - if (result == null) { |
974 | - throw new GameVersionNotExists(key.getId()); |
975 | - } |
976 | - return result; |
977 | - } finally { |
978 | - pm.close(); |
979 | + GameVersionData result = pm.getObjectById(GameVersionData.class, key); |
980 | + if (result == null) { |
981 | + throw new GameVersionNotExists(key.getId()); |
982 | } |
983 | - |
984 | + return result; |
985 | } |
986 | |
987 | } |
988 | |
989 | === added file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java' |
990 | --- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java 1970-01-01 00:00:00 +0000 |
991 | +++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java 2011-05-22 18:34:23 +0000 |
992 | @@ -0,0 +1,15 @@ |
993 | +package au.edu.unimelb.csse.mugle.shared.platform.exceptions; |
994 | + |
995 | +public class GameFileContentsNotExists extends MugleException { |
996 | + |
997 | + private static final long serialVersionUID = 728229816363066583L; |
998 | + |
999 | + public GameFileContentsNotExists() { |
1000 | + super(); |
1001 | + } |
1002 | + |
1003 | + public GameFileContentsNotExists(Long primaryKey) { |
1004 | + super("The file contents with primary key " + primaryKey.toString() + |
1005 | + " does not exist."); |
1006 | + } |
1007 | +} |
1008 | |
1009 | === modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java' |
1010 | --- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java 2011-05-20 06:35:39 +0000 |
1011 | +++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java 2011-05-22 18:34:23 +0000 |
1012 | @@ -13,7 +13,12 @@ |
1013 | } |
1014 | |
1015 | public GameFileNotExists(String gameName, String gameVersion, String path) { |
1016 | - super("The file " + path + " for the version " + gameVersion + |
1017 | - " of " + gameName + " does not Exist."); |
1018 | + super("The file " + path + " for version " + gameVersion + |
1019 | + " of " + gameName + " does not exist."); |
1020 | + } |
1021 | + |
1022 | + public GameFileNotExists(String gameVersion, String path) { |
1023 | + super("The file " + path + " for version " + gameVersion + |
1024 | + " of <unknown game> does not exist."); |
1025 | } |
1026 | } |
1027 | |
1028 | === modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java' |
1029 | --- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java 2011-05-22 07:40:42 +0000 |
1030 | +++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java 2011-05-22 18:34:23 +0000 |
1031 | @@ -38,6 +38,11 @@ |
1032 | super("Game '" + gameName + "' does not exist."); |
1033 | } |
1034 | |
1035 | + public GameNotExists(String teamName, String gameName) { |
1036 | + super("Game '" + gameName + "' by team '" + teamName |
1037 | + + "' does not exist."); |
1038 | + } |
1039 | + |
1040 | public GameNotExists(Long id) { |
1041 | super("Game with ID '" + id + "' does not exist."); |
1042 | } |
1043 | |
1044 | === added file 'war/WEB-INF/lib/commons-fileupload-1.2.2.jar' |
1045 | Binary files war/WEB-INF/lib/commons-fileupload-1.2.2.jar 1970-01-01 00:00:00 +0000 and war/WEB-INF/lib/commons-fileupload-1.2.2.jar 2011-05-22 18:34:23 +0000 differ |
1046 | === modified file 'war/WEB-INF/web.xml' |
1047 | --- war/WEB-INF/web.xml 2011-05-08 06:05:33 +0000 |
1048 | +++ war/WEB-INF/web.xml 2011-05-22 18:34:23 +0000 |
1049 | @@ -132,6 +132,28 @@ |
1050 | <servlet-name>UserGameProfileService</servlet-name> |
1051 | <url-pattern>/mugle/data-ugp</url-pattern> |
1052 | </servlet-mapping> |
1053 | + |
1054 | + <!-- Game upload and hosting services --> |
1055 | + |
1056 | + <servlet> |
1057 | + <servlet-name>UploadService</servlet-name> |
1058 | + <servlet-class>au.edu.unimelb.csse.mugle.server.UploadService</servlet-class> |
1059 | + </servlet> |
1060 | + |
1061 | + <servlet-mapping> |
1062 | + <servlet-name>UploadService</servlet-name> |
1063 | + <url-pattern>/mugle/upload</url-pattern> |
1064 | + </servlet-mapping> |
1065 | + |
1066 | + <servlet> |
1067 | + <servlet-name>GameFileServer</servlet-name> |
1068 | + <servlet-class>au.edu.unimelb.csse.mugle.server.GameFileServer</servlet-class> |
1069 | + </servlet> |
1070 | + |
1071 | + <servlet-mapping> |
1072 | + <servlet-name>GameFileServer</servlet-name> |
1073 | + <url-pattern>/+games/*</url-pattern> |
1074 | + </servlet-mapping> |
1075 | |
1076 | <!-- API servlet mappings --> |
1077 |