Merge lp:~mgiuca/mugle/gamefiles into lp:mugle

Proposed by Matt Giuca
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
Reviewer Review Type Date Requested Status
MUGLE Developers Pending
Review via email: mp+61896@code.launchpad.net

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.

To post a comment you must log in.
lp:~mgiuca/mugle/gamefiles updated
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.getGameFileContents.

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'doc/platform/urls.rst'
--- doc/platform/urls.rst 2011-05-22 08:01:47 +0000
+++ doc/platform/urls.rst 2011-05-22 18:34:23 +0000
@@ -82,13 +82,14 @@
82 / Alias for /Mugle.html82 / Alias for /Mugle.html
83 /Mugle.html The main web page83 /Mugle.html The main web page
84 /mugle/ Contains static JavaScript and Ajax URLs84 /mugle/ Contains static JavaScript and Ajax URLs
85 /upload Game upload target
85 /img/ Contains static images86 /img/ Contains static images
86 /favicon.ico87 /favicon.ico
87 /gwt-override.css88 /gwt-override.css
88 /Mugle.css89 /Mugle.css
89 /MugleIE6.css90 /MugleIE6.css
90 /<promoted-game>/+play/ Static game files for game91 /+games/<promoted-game>/ Static game files for game
91 /<team>/<game>/+play/ Static game files for game92 /+games/<team>/<game>/ Static game files for game
9293
93Client URLs94Client URLs
94-----------95-----------
9596
=== added file 'src/META-INF/mime.types'
--- src/META-INF/mime.types 1970-01-01 00:00:00 +0000
+++ src/META-INF/mime.types 2011-05-22 18:34:23 +0000
@@ -0,0 +1,35 @@
1# MUGLE MIME types mapping
2# Used to set the MIME type of files being uploaded
3# This list should be just about all the file types students will need.
4# the format is <mime type> <space separated file extensions>
5
6# Application
7application/json json
8application/javascript js
9application/pdf pdf
10application/xhtml+xml xhtml
11application/zip zip
12application/x-gzip gz gzip
13
14# Text
15text/plain txt text
16text/html htm html
17text/css css
18text/xml xml
19
20# Image
21image/gif gif
22image/jpeg jpg jpeg
23image/png png
24image/svg svg
25image/webp webp
26
27# Audio
28audio/mpeg mp3
29audio/ogg ogg
30audio/vnd.wave wav
31
32# Video
33video/mpeg mpeg
34video/mp4 mp4
35video/webm webm
036
=== modified file 'src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java'
--- src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java 2011-05-22 13:33:19 +0000
+++ src/au/edu/unimelb/csse/mugle/client/ui/GameEditBuilder.java 2011-05-22 18:34:23 +0000
@@ -16,6 +16,10 @@
16import com.google.gwt.user.client.rpc.AsyncCallback;16import com.google.gwt.user.client.rpc.AsyncCallback;
17import com.google.gwt.user.client.ui.*;17import com.google.gwt.user.client.ui.*;
18import com.google.gwt.user.client.ui.HTMLTable.RowFormatter;18import com.google.gwt.user.client.ui.HTMLTable.RowFormatter;
19import com.google.gwt.user.client.ui.FormPanel;
20import com.google.gwt.user.client.ui.FileUpload;
21
22import com.google.gwt.user.client.Window;
1923
20/**24/**
21 * Loads the view for editing game information.25 * Loads the view for editing game information.
@@ -105,6 +109,7 @@
105 panel.add(warning);109 panel.add(warning);
106 panel.add(new Label("Badges"));110 panel.add(new Label("Badges"));
107 panel.add(assembleAchievementsTable());111 panel.add(assembleAchievementsTable());
112 panel.add(assembleUploadBox(gameVersion));
108113
109 // Assemble namePanel114 // Assemble namePanel
110 namePanel.add(new Label("Name: "));115 namePanel.add(new Label("Name: "));
@@ -452,6 +457,55 @@
452 } 457 }
453 });458 });
454 }459 }
460
461 /**
462 * Creates a widget allowing the user to upload a new game in a zip file.
463 * @param version Game version to upload over the top of.
464 */
465 private Widget assembleUploadBox(GameVersion version) {
466 final FormPanel form = new FormPanel();
467 form.setAction("/mugle/upload?gameversion="
468 + Long.toString(version.getPrimaryKey()));
469
470 // File upload widget requires multipart/form-data encoding, and POST
471 // method.
472 form.setEncoding(FormPanel.ENCODING_MULTIPART);
473 form.setMethod(FormPanel.METHOD_POST);
474
475 VerticalPanel vpanel = new VerticalPanel();
476 form.setWidget(vpanel);
477 vpanel.add(new Label("Upload the game code. Instructions: Compile "
478 + "your GWT project and then zip up the 'war' directory. Your zip "
479 + "file must have a single top-level directory. Upload the zip file "
480 + "here. This will replace any existing versions of the game."));
481
482 HorizontalPanel hpanel = new HorizontalPanel();
483 vpanel.add(hpanel);
484
485 // Create a FileUpload widget
486 FileUpload upload = new FileUpload();
487 upload.setName("game_upload");
488 hpanel.add(upload);
489
490 // Add a 'submit' button
491 hpanel.add(new Button("Upload", new ClickListener() {
492 public void onClick(Widget sender) {
493 form.submit();
494 }
495 }));
496
497
498 // Handle the response from the server after upload
499 form.addFormHandler(new FormHandler() {
500 public void onSubmit(FormSubmitEvent event) {}
501
502 public void onSubmitComplete(FormSubmitCompleteEvent event) {
503 Window.alert(event.getResults());
504 }
505 });
506
507 return form;
508 }
455 509
456 /**510 /**
457 * Constructs the Navigation bar down the bottom (with delete button)511 * Constructs the Navigation bar down the bottom (with delete button)
458512
=== modified file 'src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java'
--- src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java 2011-05-22 13:10:42 +0000
+++ src/au/edu/unimelb/csse/mugle/server/DataTestServiceImpl.java 2011-05-22 18:34:23 +0000
@@ -100,7 +100,6 @@
100100
101 gamefiles[0].setPath("index.html");101 gamefiles[0].setPath("index.html");
102 gamefiles[0].setMimeType("text/html");102 gamefiles[0].setMimeType("text/html");
103 gamefiles[0].setBlobKey(null); // XXX
104 gamefiles[0].setGameVersionKey(gameversions[0].getPrimaryKey());103 gamefiles[0].setGameVersionKey(gameversions[0].getPrimaryKey());
105104
106 GameFileServiceImpl gfs = new GameFileServiceImpl();105 GameFileServiceImpl gfs = new GameFileServiceImpl();
107106
=== modified file 'src/au/edu/unimelb/csse/mugle/server/GameFileServer.java'
--- src/au/edu/unimelb/csse/mugle/server/GameFileServer.java 2011-05-20 06:35:39 +0000
+++ src/au/edu/unimelb/csse/mugle/server/GameFileServer.java 2011-05-22 18:34:23 +0000
@@ -16,37 +16,92 @@
16 */16 */
17package au.edu.unimelb.csse.mugle.server;17package au.edu.unimelb.csse.mugle.server;
1818
19import java.io.OutputStream;
19import java.io.IOException;20import java.io.IOException;
2021
21import javax.servlet.http.HttpServlet;22import javax.servlet.http.HttpServlet;
22import javax.servlet.http.HttpServletRequest;23import javax.servlet.http.HttpServletRequest;
23import javax.servlet.http.HttpServletResponse;24import javax.servlet.http.HttpServletResponse;
2425
26import com.google.appengine.api.datastore.Key;
27
28import au.edu.unimelb.csse.mugle.server.model.GameData;
29import au.edu.unimelb.csse.mugle.server.model.GameGetter;
30import au.edu.unimelb.csse.mugle.server.model.GameVersionData;
31import au.edu.unimelb.csse.mugle.server.model.GameVersionGetter;
25import au.edu.unimelb.csse.mugle.server.model.GameFileData;32import au.edu.unimelb.csse.mugle.server.model.GameFileData;
26import au.edu.unimelb.csse.mugle.server.model.GameFileGetter;33import au.edu.unimelb.csse.mugle.server.model.GameFileGetter;
34import au.edu.unimelb.csse.mugle.server.model.GameFileContents;
35import au.edu.unimelb.csse.mugle.shared.platform.exceptions.DevTeamNotExists;
27import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;36import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;
37import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileContentsNotExists;
28import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;38import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
29import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;39import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;
3040
31import com.google.appengine.api.blobstore.BlobKey;
32import com.google.appengine.api.blobstore.BlobstoreService;
33import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
34
35@SuppressWarnings("serial")41@SuppressWarnings("serial")
36public class GameFileServer extends HttpServlet {42public class GameFileServer extends HttpServlet {
37 43
38 public void doGet(HttpServletRequest req, HttpServletResponse res) 44 public void doGet(HttpServletRequest req, HttpServletResponse res)
39 throws IOException {45 throws IOException {
40 BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();46 String path = req.getPathInfo();
41 47 if (path.length() > 0 && path.charAt(0) == '/')
42 try {48 path = path.substring(1);
43 BlobKey blobKey = getBlobKey(req);49 String[] pathSegments = path.split("/", 3);
44 blobstoreService.serve(blobKey, res);50 // XXX Assumes a devteam/game, not a promoted game
45 } catch (GameFileNotExists e) {51 if (pathSegments.length != 3)
46 //TODO: Construct a page with the error52 {
47 }53 res.sendError(404, "Not found");
54 return;
55 }
56 String devteamName = pathSegments[0];
57 String gameName = pathSegments[1];
58 String filePath = pathSegments[2];
59
60 GameFileData file;
61 GameFileContents contents;
62 try
63 {
64 GameData game = GameGetter.getGameByDevTeam(devteamName,gameName);
65 Key versionKey = game.getActiveVersion();
66 GameVersionData version =
67 GameVersionGetter.getGameVersion(versionKey);
68 file = GameFileGetter.getGameFile(game, version, filePath);
69 contents = GameFileGetter.getGameFileContents(file);
70 }
71 catch (DevTeamNotExists e)
72 {
73 res.sendError(404, e.toString());
74 return;
75 }
76 catch (GameNotExists e)
77 {
78 res.sendError(404, e.toString());
79 return;
80 }
81 catch (GameVersionNotExists e)
82 {
83 res.sendError(404, e.toString());
84 return;
85 }
86 catch (GameFileNotExists e)
87 {
88 res.sendError(404, e.toString());
89 return;
90 }
91 catch (GameFileContentsNotExists e)
92 {
93 res.sendError(404, e.toString());
94 return;
95 }
96
97 // Serve the file on the HTTP response
98 res.setContentType(file.getMimeType());
99 byte[] bytes = contents.getContents();
100 res.setContentLength(bytes.length);
101 res.getOutputStream().write(bytes);
48 }102 }
49103
104 /*
50 private BlobKey getBlobKey(HttpServletRequest req) 105 private BlobKey getBlobKey(HttpServletRequest req)
51 throws GameFileNotExists {106 throws GameFileNotExists {
52 107
@@ -57,7 +112,7 @@
57 /* We need to parse the request to get the gameName, gameVersion112 /* We need to parse the request to get the gameName, gameVersion
58 * and path,113 * and path,
59 * URLs are in the form /game/gameName/gameVersion/path114 * URLs are in the form /game/gameName/gameVersion/path
60 */115 * /
61 String toParse = req.getRequestURI().split("/game/")[1]; // gets everything after /game/ in the URI116 String toParse = req.getRequestURI().split("/game/")[1]; // gets everything after /game/ in the URI
62 String[] splitted = toParse.split("/");117 String[] splitted = toParse.split("/");
63 gameName = splitted[0];118 gameName = splitted[0];
@@ -74,6 +129,7 @@
74 }129 }
75 130
76 }131 }
132 */
77133
78 /*134 /*
79 public void doPost(HttpServletRequest req, HttpServletResponse res) 135 public void doPost(HttpServletRequest req, HttpServletResponse res)
80136
=== added file 'src/au/edu/unimelb/csse/mugle/server/UploadService.java'
--- src/au/edu/unimelb/csse/mugle/server/UploadService.java 1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/UploadService.java 2011-05-22 18:34:23 +0000
@@ -0,0 +1,197 @@
1/* Melbourne University Game-based Learning Environment
2 * Copyright (C) 2011 The University of Melbourne
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17package au.edu.unimelb.csse.mugle.server;
18
19import java.io.InputStream;
20import java.io.PrintWriter;
21import java.io.IOException;
22import javax.servlet.http.HttpServlet;
23import javax.servlet.http.HttpServletRequest;
24import javax.servlet.http.HttpServletResponse;
25import javax.jdo.PersistenceManager;
26
27import java.util.zip.ZipInputStream;
28import java.util.zip.ZipEntry;
29import java.util.zip.ZipException;
30
31import org.apache.commons.fileupload.FileItemIterator;
32import org.apache.commons.fileupload.FileItemStream;
33import org.apache.commons.fileupload.servlet.ServletFileUpload;
34import org.apache.commons.fileupload.FileUploadException;
35
36import java.util.Vector;
37
38import au.edu.unimelb.csse.mugle.server.model.GameFileData;
39import au.edu.unimelb.csse.mugle.server.model.GameFileContents;
40import au.edu.unimelb.csse.mugle.server.model.GameFileGetter;
41import au.edu.unimelb.csse.mugle.server.model.GameVersionData;
42import au.edu.unimelb.csse.mugle.server.PMF;
43import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;
44import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileContentsNotExists;
45
46public class UploadService extends HttpServlet
47{
48 public void doPost(HttpServletRequest request,
49 HttpServletResponse response) throws IOException
50 {
51 long gameversionID = Long.parseLong(
52 request.getParameter("gameversion"));
53 GameVersionData gameversion;
54 boolean got_zip = false;
55 int numfiles = 0;
56 Vector<String> filenames = new Vector<String>();
57
58 // Read the POST data fields
59 ServletFileUpload upload = new ServletFileUpload();
60 PersistenceManager pm = PMF.getManager();
61 try
62 {
63 // Get the GameVersion object
64 gameversion =
65 pm.getObjectById(GameVersionData.class, gameversionID);
66 }
67 finally
68 {
69 pm.close();
70 }
71 try
72 {
73 // For each field
74 FileItemIterator iter = upload.getItemIterator(request);
75 while (iter.hasNext())
76 {
77 FileItemStream item = iter.next();
78 // Only care about the game_upload field
79 if (!item.getFieldName().equals("game_upload"))
80 continue;
81 got_zip = true;
82
83 // Assume it is a ZIP file. Read each file contained inside.
84 InputStream stream = item.openStream();
85 ZipInputStream zstream = new ZipInputStream(stream);
86 ZipEntry entry;
87 while ((entry = zstream.getNextEntry()) != null)
88 {
89 if (entry.isDirectory())
90 {
91 // Ignore directories (since we are not building a
92 // tree; just remembering the path to each file)
93 continue;
94 }
95 String filename = entry.getName();
96 // Remove the first path segment from the filename (since
97 // ZIP files tend to have a top-level directory, and we
98 // want to explode out of that).
99 String[] filename_split = filename.split("/", 2);
100 if (filename_split.length > 1)
101 filename = filename_split[1];
102 long size = entry.getSize();
103 // Create a new GameFile object
104 createGameFile(gameversion, filename, zstream, size);
105 filenames.add(filename);
106 numfiles++;
107 }
108 }
109 }
110 catch (FileUploadException e)
111 {
112 response.sendError(400, e.toString());
113 return;
114 }
115 catch (ZipException e)
116 {
117 response.sendError(400, e.toString());
118 return;
119 }
120 if (!got_zip)
121 {
122 response.sendError(400, "Missing POST field game_upload");
123 return;
124 }
125
126 // Set HTML content type, even though returning text
127 // The result is shown in an alert, and if it's text, Firefox will add
128 // "<pre>".
129 response.setContentType("text/html");
130 PrintWriter out = response.getWriter();
131 out.println("Successfully uploaded a ZIP file with "
132 + numfiles + " files:");
133 for (String s : filenames)
134 out.println("- " + s);
135 }
136
137 /** Create a new GameFile and store it in the database, from a part of a
138 * Zip file.
139 * @param gameVersionKey The game version to upload this file to.
140 * @param filePath Path to the file relative to this game version.
141 * @param data Binary stream to read file contents from.
142 * @param dataSize Number of bytes to read from data.
143 */
144 public static void createGameFile(GameVersionData gameVersion,
145 String filePath, InputStream data, long dataSize)
146 throws IOException
147 {
148 PersistenceManager pm = PMF.getManager();
149 try
150 {
151 // Check if a file already exists at that path
152 GameFileData file;
153 GameFileContents contents;
154 try
155 {
156 file = GameFileGetter.getGameFile(pm, gameVersion, filePath);
157 }
158 catch (GameFileNotExists e)
159 {
160 // First, store the contents as a blob in the database
161 contents = new GameFileContents(data, dataSize);
162 pm.makePersistent(contents);
163 // Then, create a GameFile which points at that blob
164 file = new GameFileData(gameVersion, filePath, contents);
165 pm.makePersistent(file);
166 return;
167 }
168 // Already exists; just update the file and contents
169 // (Note that if we were uploading a new version, we wouldn't
170 // overwrite the file or contents, but since we're updating an
171 // existing version, we can replace the contents).
172 // XXX Note that if contents are shared across versions this will
173 // clobber ALL versions, so probably we should create a new
174 // content object each time.
175 try
176 {
177 contents = GameFileGetter.getGameFileContents(file);
178 contents.setContents(data, dataSize);
179 pm.makePersistent(contents);
180 }
181 catch (GameFileContentsNotExists e)
182 {
183 contents = new GameFileContents(data, dataSize);
184 pm.makePersistent(contents);
185 file.setContents(contents.getServerKey());
186 }
187 // Also set the MIME type based on the filename (so the result is
188 // consistent with what would have happened if the file didn't
189 // exist).
190 file.setMimeTypeFromFilename();
191 }
192 finally
193 {
194 pm.close();
195 }
196 }
197}
0198
=== added file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java 1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameFileContents.java 2011-05-22 18:34:23 +0000
@@ -0,0 +1,104 @@
1/* Melbourne University Game-based Learning Environment
2 * Copyright (C) 2011 The University of Melbourne
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18package au.edu.unimelb.csse.mugle.server.model;
19
20import java.io.OutputStream;
21import java.io.InputStream;
22import java.io.IOException;
23
24import javax.jdo.PersistenceManager;
25import javax.jdo.annotations.IdGeneratorStrategy;
26import javax.jdo.annotations.PersistenceCapable;
27import javax.jdo.annotations.Persistent;
28import javax.jdo.annotations.PrimaryKey;
29
30import com.google.appengine.api.datastore.Key;
31import com.google.appengine.api.datastore.Blob;
32
33/** The contents of a GameFile.
34 * This simple class contains only the contents and no other meta-data. It
35 * may be referenced by one or more GameFile objects.
36 */
37@PersistenceCapable
38public class GameFileContents {
39 // Fields
40
41 @PrimaryKey
42 @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
43 private Key id;
44
45 @Persistent
46 private Blob data;
47
48 /** Create a new file contents blob, from a given input stream.
49 @param size The number of bytes to read from stream.
50 */
51 public GameFileContents(InputStream stream, long size)
52 throws IOException
53 {
54 this.setContents(stream, size);
55 }
56
57 // Getters
58
59 public Key getServerKey() {
60 return this.id;
61 }
62
63 // Misc methods
64
65 /** Get the contents of the file as a byte array.
66 */
67 public byte[] getContents()
68 {
69 return this.data.getBytes();
70 }
71
72 /** Write the contents of the file to the OutputStream object.
73 * @param writer Stream object to receive the contents of the file.
74 */
75 public void getContents(OutputStream writer)
76 throws IOException
77 {
78 byte[] bytes = this.data.getBytes();
79 writer.write(bytes);
80 }
81
82 /** Read the contents of an InputStream and store it in the database as
83 * the contents of this file.
84 * @param stream Stream object to read contents of file from.
85 @param size The number of bytes to read from stream.
86 */
87 public void setContents(InputStream stream, long size)
88 throws IOException
89 {
90 //TODO: check that the DevTeam's space limit hasn't been exceeded.
91 byte[] bytes = new byte[(int)size];
92 // Read in a loop
93 // (stream.read might read fewer than the requested number of bytes)
94 int offset = 0;
95 int remaining = (int)size;
96 while (remaining > 0)
97 {
98 int bytes_read = stream.read(bytes, offset, remaining);
99 offset += bytes_read;
100 remaining -= bytes_read;
101 }
102 this.data = new Blob(bytes);
103 }
104}
0105
=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java 2011-05-22 11:06:46 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameFileData.java 2011-05-22 18:34:23 +0000
@@ -24,9 +24,10 @@
24import javax.jdo.annotations.PersistenceCapable;24import javax.jdo.annotations.PersistenceCapable;
25import javax.jdo.annotations.Persistent;25import javax.jdo.annotations.Persistent;
26import javax.jdo.annotations.PrimaryKey;26import javax.jdo.annotations.PrimaryKey;
27import javax.activation.MimetypesFileTypeMap;
2728
28import com.google.appengine.api.blobstore.BlobKey;
29import com.google.appengine.api.datastore.Key;29import com.google.appengine.api.datastore.Key;
30import com.google.appengine.api.datastore.Blob;
3031
31import au.edu.unimelb.csse.mugle.shared.model.*;32import au.edu.unimelb.csse.mugle.shared.model.*;
32import au.edu.unimelb.csse.mugle.server.PMF;33import au.edu.unimelb.csse.mugle.server.PMF;
@@ -50,7 +51,7 @@
50 private String mimeType; //mime type of the file51 private String mimeType; //mime type of the file
5152
52 @Persistent53 @Persistent
53 private BlobKey blobKey; //key to the actual blob -- may be shared between GameFiles across versions54 private Key contents; //key to the GameFileContents
5455
55 @Persistent56 @Persistent
56 private Key version; //Game version this Gamefile belongs to57 private Key version; //Game version this Gamefile belongs to
@@ -72,23 +73,43 @@
72 public GameFileData() {73 public GameFileData() {
73 this.version = null;74 this.version = null;
74 this.setMimeType(null);75 this.setMimeType(null);
75 this.setBlobKey(null);
76 this.path = null;76 this.path = null;
77 }77 }
7878
79/** TODO: Pending Blob implementation for files79 /** Create a new GameFileData object.
80 // For creation of the file for the first time80 * Guesses the file's MIME type based on the file extension.
81 public GameFile (String path, GameVersion version, InputStream contents) {81 * @param version The game version the file belongs to.
82 this.path = path;82 * @param path Path to the file relative to this gameversion.
83 this.versions.add(version.getPrimaryKey());83 * @param contents Blob containing the file's contents.
84 this.setContents(contents);84 */
85 }85 public GameFileData(GameVersionData version, String path,
86*/86 GameFileContents contents)
87 {
88 this.version = version.getServerKey();
89 this.path = path;
90 this.setMimeTypeFromFilename();
91 this.contents = contents.getServerKey();
92 }
93
94 /** Create a new GameFileData object.
95 * @param version The game version the file belongs to.
96 * @param path Path to the file relative to this gameversion.
97 * @param mimeType MIME type of the file.
98 * @param contents Blob containing the file's contents.
99 */
100 public GameFileData(GameVersionData version, String path, String mimeType,
101 GameFileContents contents)
102 {
103 this.version = version.getServerKey();
104 this.path = path;
105 this.mimeType = mimeType;
106 this.contents = contents.getServerKey();
107 }
87108
88 /* When a file is moved, or renamed - The existing GameFile entry should be deleted,109 /* When a file is moved, or renamed - The existing GameFile entry should be deleted,
89 * in which case, we may want to initialise with multiple Game versions.110 * in which case, we may want to initialise with multiple Game versions.
90 */111 */
91/** TODO: Pending Blob implementation for files112/** TODO: Pending multiple versions of games
92 public GameFile (String path, Set<String> versions, InputStream contents) {113 public GameFile (String path, Set<String> versions, InputStream contents) {
93 this.path = path;114 this.path = path;
94 this.versions = versions;115 this.versions = versions;
@@ -122,18 +143,6 @@
122 return mimeType;143 return mimeType;
123 }144 }
124145
125 @GetterUserLevel(privateView=Role.GUEST, publicView=Role.GUEST, mappedTo="setBlobKey", overrideBy="getBlobKeyString")
126 public BlobKey getBlobKey() {
127 return this.blobKey;
128 }
129
130 public String getBlobKeyString() {
131 if (this.blobKey == null) {
132 return null;
133 }
134 return this.blobKey.getKeyString();
135 }
136
137 @Override146 @Override
138 @GetterUserLevel(privateView=Role.GUEST, publicView=Role.GUEST, mappedTo="setCreatedUser", overrideBy="keyToCreated")147 @GetterUserLevel(privateView=Role.GUEST, publicView=Role.GUEST, mappedTo="setCreatedUser", overrideBy="keyToCreated")
139 public Key getCreatedUser() {148 public Key getCreatedUser() {
@@ -209,40 +218,30 @@
209 this.mimeType = mimeType;218 this.mimeType = mimeType;
210 }219 }
211220
212 // TODO: do we allow the clients to modify this value
213 //@SetterUserLevel(getter="", mappedBy="")
214 public void setBlobKey(BlobKey blobKey) {
215 this.blobKey = blobKey;
216 }
217
218 // Misc methods221 // Misc methods
219222
220 /** Write the contents of the file to the PrintWriter object.223 /** Get the content blob of this file.
221 * @param writer Stream object to receive the contents of the file.224 * @return Key of the GameFileContents object.
222 */225 */
223/** TODO: Pending Blob implementation for files226 public Key getContents() {
224 public void getContents(PrintWriter writer) {227 return this.contents;
225 //TODO: retrieve file from database228 }
226 }229
227*/230 /** Set the content blob of this file.
228231 * @param contents Key of the GameFileContents object.
229 /** Read the contents of an InputStream and store it in the database as232 */
230 * the contents of this file.233 public void setContents(Key contents) {
231 * @param stream Stream object to read contents of file from.234 this.contents = contents;
232 */235 }
233/** TODO: Pending Blob implementation for files236
234 public void setContents(InputStream stream) {237 /** Set the MIME type based on the file extension.
235 //TODO: add/create the file in the database238 */
236 //TODO: check that the DevTeam's space limit hasn't been exceeded.239 public void setMimeTypeFromFilename() {
237 }240 String f = this.path.toLowerCase();
238*/241 // This implicitly looks at the MIME mapping table in
239242 // src/META-INF/mime.types.
240243 this.mimeType = new MimetypesFileTypeMap().getContentType(this.path);
241/** TODO: Pending Blob implementation for files244 }
242 public GameFileData(String path, GameVersion version, InputStream contents) {
243 super(path, version, contents);
244 }
245*/
246245
247 @Override246 @Override
248 public Key getServerKey() {247 public Key getServerKey() {
249248
=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java 2011-05-22 11:06:46 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameFileGetter.java 2011-05-22 18:34:23 +0000
@@ -27,6 +27,7 @@
2727
28import au.edu.unimelb.csse.mugle.server.PMF;28import au.edu.unimelb.csse.mugle.server.PMF;
29import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;29import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileNotExists;
30import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameFileContentsNotExists;
30import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;31import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameNotExists;
31import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;32import au.edu.unimelb.csse.mugle.shared.platform.exceptions.GameVersionNotExists;
3233
@@ -114,6 +115,27 @@
114 115
115 /**116 /**
116 * Gets the GameFile by its path, version and game, for editing object in datastore117 * Gets the GameFile by its path, version and game, for editing object in datastore
118 * @param gameName the name of the game
119 * @param gameVersion the version of the game
120 * @param path the path of the file
121 * @return the GameFile - read only
122 * @throws GameNotExists
123 * @throws GameVersionNotExists
124 * @throws GameFileNotExists
125 */
126 @SuppressWarnings("unchecked")
127 public static GameFileData getGameFile(GameData game, GameVersionData gameVersion, String path)
128 throws GameFileNotExists {
129 PersistenceManager pm = PMF.getManager();
130 try {
131 return getGameFile(pm, game, gameVersion, path);
132 } finally {
133 pm.close();
134 }
135 }
136
137 /**
138 * Gets the GameFile by its path, version and game, for editing object in datastore
117 * PersistenceManager must be handled by the caller139 * PersistenceManager must be handled by the caller
118 * @para pm The Persistence Manager140 * @para pm The Persistence Manager
119 * @param gameName the name of the game141 * @param gameName the name of the game
@@ -126,14 +148,15 @@
126 */148 */
127 @SuppressWarnings("unchecked")149 @SuppressWarnings("unchecked")
128 public static GameFileData getGameFile(PersistenceManager pm, GameData game, GameVersionData gameVersion, String path)150 public static GameFileData getGameFile(PersistenceManager pm, GameData game, GameVersionData gameVersion, String path)
129 throws GameFileNotExists, GameNotExists, GameVersionNotExists {151 throws GameFileNotExists {
130152
131 Query q = pm.newQuery(GameFileData.class, "path == p && version == v");153 Query q = pm.newQuery(GameFileData.class, "path == p && version == v");
132 q.declareParameters("String p, com.google.appengine.api.datastore.Key v");154 q.declareParameters("String p, com.google.appengine.api.datastore.Key v");
133 List<GameFileData> results = (List<GameFileData>) q.execute(path, gameVersion.getServerKey());155 List<GameFileData> results = (List<GameFileData>) q.execute(path, gameVersion.getServerKey());
134156
135 if (results.isEmpty()) {157 if (results.isEmpty()) {
136 throw new GameFileNotExists(game.getUrlName(), String.valueOf(gameVersion), path);158 throw new GameFileNotExists(game.getUrlName(),
159 gameVersion.getDisplayVersion(), path);
137 }160 }
138 return results.get(0);161 return results.get(0);
139 }162 }
@@ -160,13 +183,41 @@
160 List<GameFileData> results = (List<GameFileData>) q.execute(path, gvd.getServerKey());183 List<GameFileData> results = (List<GameFileData>) q.execute(path, gvd.getServerKey());
161184
162 if (results.isEmpty()) {185 if (results.isEmpty()) {
163 throw new GameFileNotExists(game.getUrlName(), String.valueOf(gameVersion), path);186 throw new GameFileNotExists(game.getUrlName(),
187 Integer.toString(gameVersion), path);
164 }188 }
165 189
166 return results.get(0);190 return results.get(0);
167 }191 }
168 192
169 /**193 /**
194 * Gets the GameFile by its path and version, for editing object in
195 * datastore.
196 * PersistenceManager must be handled by the caller
197 * @para pm The Persistence Manager
198 * @param gameVersion the version of the game
199 * @param path the path of the file
200 * @return the GameFile - read only
201 * @throws GameFileNotExists
202 */
203 @SuppressWarnings("unchecked")
204 public static GameFileData getGameFile(PersistenceManager pm,
205 GameVersionData gameVersion, String path)
206 throws GameFileNotExists {
207
208 Query q = pm.newQuery(GameFileData.class, "path == p && version == v");
209 q.declareParameters("String p, com.google.appengine.api.datastore.Key v");
210 List<GameFileData> results = (List<GameFileData>) q.execute(path,
211 gameVersion.getServerKey());
212
213 if (results.isEmpty()) {
214 throw new GameFileNotExists(gameVersion.getDisplayVersion(), path);
215 }
216
217 return results.get(0);
218 }
219
220 /**
170 * Gets the GameFile associated with the given GameFileData primary key.221 * Gets the GameFile associated with the given GameFileData primary key.
171 * @param pm The PersistenceManager222 * @param pm The PersistenceManager
172 * @param key The server-side key (ModelDataClass.getServerKey())223 * @param key The server-side key (ModelDataClass.getServerKey())
@@ -187,5 +238,39 @@
187 }238 }
188 239
189 }240 }
190 241
242 /**
243 * Gets the GameFileContents associated with the given GameFile.
244 * @param file The file to retrieve contents for.
245 * @return The GameFileContents object directly off the datastore.
246 * @throws GameFileContentsNotExists
247 */
248 public static GameFileContents getGameFileContents(GameFileData file)
249 throws GameFileContentsNotExists {
250 PersistenceManager pm = PMF.getManager();
251 try {
252 return getGameFileContents(pm, file);
253 } finally {
254 pm.close();
255 }
256 }
257
258 /**
259 * Gets the GameFileContents associated with the given GameFile.
260 * @param pm The PersistenceManager
261 * @param file The file to retrieve contents for.
262 * @return The GameFileContents object directly off the datastore.
263 * @throws GameFileContentsNotExists
264 */
265 public static GameFileContents getGameFileContents(PersistenceManager pm,
266 GameFileData file) throws GameFileContentsNotExists {
267 Key key = file.getContents();
268 GameFileContents result = pm.getObjectById(GameFileContents.class,
269 key);
270 if (result == null)
271 {
272 throw new GameFileContentsNotExists(key.getId());
273 }
274 return result;
275 }
191}276}
192277
=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java 2011-05-20 06:35:39 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameGetter.java 2011-05-22 18:34:23 +0000
@@ -185,15 +185,17 @@
185 185
186 /**186 /**
187 * Gets the Game by the name of the DevTeam it belongs to - READ ONLY187 * Gets the Game by the name of the DevTeam it belongs to - READ ONLY
188 * @param name name of the DevTeam188 * @param devTeamName name of the DevTeam
189 * @param gameName name of the Game
189 * @return the Game - read only190 * @return the Game - read only
190 * @throws GameNotExists 191 * @throws GameNotExists
191 * @throws DevTeamNotExists 192 * @throws DevTeamNotExists
192 */193 */
193 public static GameData getGameByDevTeam(String name) throws GameNotExists, DevTeamNotExists {194 public static GameData getGameByDevTeam(String devTeamName,
195 String gameName) throws GameNotExists, DevTeamNotExists {
194 PersistenceManager pm = PMF.getManager();196 PersistenceManager pm = PMF.getManager();
195 try {197 try {
196 return getGameByDevTeam(pm, name);198 return getGameByDevTeam(pm, devTeamName, gameName);
197 } finally {199 } finally {
198 pm.close();200 pm.close();
199 }201 }
@@ -209,14 +211,17 @@
209 * @throws DevTeamNotExists 211 * @throws DevTeamNotExists
210 */212 */
211 @SuppressWarnings("unchecked")213 @SuppressWarnings("unchecked")
212 public static GameData getGameByDevTeam(PersistenceManager pm, String name) throws GameNotExists, DevTeamNotExists{214 public static GameData getGameByDevTeam(PersistenceManager pm,
213 DevTeamData dtd = DevTeamGetter.getDevTeam(name);215 String devTeamName, String gameName)
214 Query q = pm.newQuery(GameData.class, "devTeam == d");216 throws GameNotExists, DevTeamNotExists{
215 q.declareParameters("com.google.appengine.api.datastore.Key d");217 DevTeamData dtd = DevTeamGetter.getDevTeam(devTeamName);
216 List<GameData> results = (List<GameData>) q.execute(dtd.getServerKey());218 Query q = pm.newQuery(GameData.class, "devTeam == d && urlName == g");
219 q.declareParameters("com.google.appengine.api.datastore.Key d, String g");
220 List<GameData> results = (List<GameData>)
221 q.execute(dtd.getServerKey(), gameName);
217 222
218 if(results.isEmpty()) {223 if(results.isEmpty()) {
219 throw new GameNotExists(name);224 throw new GameNotExists(devTeamName, gameName);
220 }225 }
221 226
222 return results.get(0);227 return results.get(0);
223228
=== modified file 'src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java'
--- src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java 2011-05-22 07:40:42 +0000
+++ src/au/edu/unimelb/csse/mugle/server/model/GameVersionGetter.java 2011-05-22 18:34:23 +0000
@@ -155,6 +155,21 @@
155 }155 }
156 156
157 /**157 /**
158 * Gets the GameVersion by its Primary Key - READ ONLY
159 * @param primaryKey
160 * @return the GameVersion - read only
161 * @throws GameVersionNotExists
162 */
163 public static GameVersionData getGameVersion(Key primaryKey) throws GameVersionNotExists {
164 PersistenceManager pm = PMF.getManager();
165 try {
166 return getGameVersion(pm, primaryKey);
167 } finally {
168 pm.close();
169 }
170 }
171
172 /**
158 * Gets the GameVersion associated with the given GameVersionData primary key.173 * Gets the GameVersion associated with the given GameVersionData primary key.
159 * @param pm The PersistenceManager174 * @param pm The PersistenceManager
160 * @param key The server-side key (ModelDataClass.getServerKey())175 * @param key The server-side key (ModelDataClass.getServerKey())
@@ -164,16 +179,11 @@
164 public static GameVersionData getGameVersion(PersistenceManager pm, Key key)179 public static GameVersionData getGameVersion(PersistenceManager pm, Key key)
165 throws GameVersionNotExists {180 throws GameVersionNotExists {
166 181
167 try {182 GameVersionData result = pm.getObjectById(GameVersionData.class, key);
168 GameVersionData result = pm.getObjectById(GameVersionData.class, key);183 if (result == null) {
169 if (result == null) {184 throw new GameVersionNotExists(key.getId());
170 throw new GameVersionNotExists(key.getId());
171 }
172 return result;
173 } finally {
174 pm.close();
175 }185 }
176 186 return result;
177 }187 }
178 188
179}189}
180190
=== added file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java 1970-01-01 00:00:00 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileContentsNotExists.java 2011-05-22 18:34:23 +0000
@@ -0,0 +1,15 @@
1package au.edu.unimelb.csse.mugle.shared.platform.exceptions;
2
3public class GameFileContentsNotExists extends MugleException {
4
5 private static final long serialVersionUID = 728229816363066583L;
6
7 public GameFileContentsNotExists() {
8 super();
9 }
10
11 public GameFileContentsNotExists(Long primaryKey) {
12 super("The file contents with primary key " + primaryKey.toString() +
13 " does not exist.");
14 }
15}
016
=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java 2011-05-20 06:35:39 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameFileNotExists.java 2011-05-22 18:34:23 +0000
@@ -13,7 +13,12 @@
13 }13 }
14 14
15 public GameFileNotExists(String gameName, String gameVersion, String path) {15 public GameFileNotExists(String gameName, String gameVersion, String path) {
16 super("The file " + path + " for the version " + gameVersion + 16 super("The file " + path + " for version " + gameVersion +
17 " of " + gameName + " does not Exist.");17 " of " + gameName + " does not exist.");
18 }
19
20 public GameFileNotExists(String gameVersion, String path) {
21 super("The file " + path + " for version " + gameVersion +
22 " of <unknown game> does not exist.");
18 }23 }
19}24}
2025
=== modified file 'src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java'
--- src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java 2011-05-22 07:40:42 +0000
+++ src/au/edu/unimelb/csse/mugle/shared/platform/exceptions/GameNotExists.java 2011-05-22 18:34:23 +0000
@@ -38,6 +38,11 @@
38 super("Game '" + gameName + "' does not exist.");38 super("Game '" + gameName + "' does not exist.");
39 }39 }
40 40
41 public GameNotExists(String teamName, String gameName) {
42 super("Game '" + gameName + "' by team '" + teamName
43 + "' does not exist.");
44 }
45
41 public GameNotExists(Long id) {46 public GameNotExists(Long id) {
42 super("Game with ID '" + id + "' does not exist.");47 super("Game with ID '" + id + "' does not exist.");
43 }48 }
4449
=== added file 'war/WEB-INF/lib/commons-fileupload-1.2.2.jar'
45Binary 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 differ50Binary 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
=== modified file 'war/WEB-INF/web.xml'
--- war/WEB-INF/web.xml 2011-05-08 06:05:33 +0000
+++ war/WEB-INF/web.xml 2011-05-22 18:34:23 +0000
@@ -132,6 +132,28 @@
132 <servlet-name>UserGameProfileService</servlet-name>132 <servlet-name>UserGameProfileService</servlet-name>
133 <url-pattern>/mugle/data-ugp</url-pattern>133 <url-pattern>/mugle/data-ugp</url-pattern>
134 </servlet-mapping>134 </servlet-mapping>
135
136 <!-- Game upload and hosting services -->
137
138 <servlet>
139 <servlet-name>UploadService</servlet-name>
140 <servlet-class>au.edu.unimelb.csse.mugle.server.UploadService</servlet-class>
141 </servlet>
142
143 <servlet-mapping>
144 <servlet-name>UploadService</servlet-name>
145 <url-pattern>/mugle/upload</url-pattern>
146 </servlet-mapping>
147
148 <servlet>
149 <servlet-name>GameFileServer</servlet-name>
150 <servlet-class>au.edu.unimelb.csse.mugle.server.GameFileServer</servlet-class>
151 </servlet>
152
153 <servlet-mapping>
154 <servlet-name>GameFileServer</servlet-name>
155 <url-pattern>/+games/*</url-pattern>
156 </servlet-mapping>
135 157
136 <!-- API servlet mappings -->158 <!-- API servlet mappings -->
137 159

Subscribers

People subscribed via source and target branches