elastic-grid
Rev 472 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/**
* Elastic Grid
* Copyright (C) 2008-2010 Elastic Grid, LLC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.elasticgrid.rest;
import com.elasticgrid.cluster.ClusterManager;
import com.elasticgrid.model.Cluster;
import com.elasticgrid.model.internal.Applications;
import com.elasticgrid.utils.amazon.AWSUtils;
import com.elasticgrid.storage.Container;
import com.elasticgrid.storage.StorageException;
import com.elasticgrid.storage.StorageManager;
import com.elasticgrid.storage.spi.StorageEngine;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.restlet.Context;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.ext.freemarker.TemplateRepresentation;
import org.restlet.ext.jibx.JibxRepresentation;
import org.restlet.ext.wadl.DocumentationInfo;
import org.restlet.ext.wadl.MethodInfo;
import org.restlet.ext.wadl.RepresentationInfo;
import org.restlet.ext.wadl.WadlResource;
import org.restlet.resource.Representation;
import org.restlet.resource.ResourceException;
import org.restlet.resource.Variant;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ApplicationsResource extends WadlResource {
private String clusterName;
private String dropBucket;
private Configuration config;
private ClusterManager clusterManager;
private StorageManager storageManager;
private final Logger logger = Logger.getLogger(getClass().getName());
@Override
public void init(Context context, Request request, Response response) {
super.init(context, request, response);
clusterManager = RestJSB.getClusterManager();
storageManager = RestJSB.getStorageManager();
try {
dropBucket = AWSUtils.getDropBucket();
} catch (IOException e) {
throw new IllegalStateException("Can't retrieve drop bucket", e);
}
// Allow modifications of this resource via POST requests
setModifiable(true);
// Declare the kind of representations supported by this resource
getVariants().add(new Variant(MediaType.APPLICATION_XML));
// Extract URI variables
clusterName = (String) request.getAttributes().get("clusterName");
// Setup FreeMarker template engine
config = new Configuration();
config.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
config.setTemplateLoader(new ClassTemplateLoader(getClass(), "/com/elasticgrid/rest"));
}
/**
* Handle GET requests: describe all applications.
*/
@Override
public Representation represent(Variant variant) throws ResourceException {
try {
logger.log(Level.INFO, "Requested variant {0}", variant.getMediaType());
Cluster cluster = clusterManager.cluster(clusterName);
if (cluster == null)
throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "Can't find cluster " + clusterName);
logger.log(Level.INFO, "Found cluster {0}", cluster);
if (MediaType.TEXT_HTML.equals(variant.getMediaType())
|| MediaType.APPLICATION_XML.equals(variant.getMediaType())) {
Map<String, Object> model = new HashMap<String, Object>();
model.put("cluster", cluster);
return new TemplateRepresentation("applications.ftl", config, model, MediaType.TEXT_HTML);
} else {
// return XML representation
return new JibxRepresentation<Applications>(MediaType.APPLICATION_XML,
new Applications(cluster.getApplications()), "ElasticGridREST");
}
} catch (Exception e) {
e.printStackTrace();
throw new ResourceException(Status.SERVER_ERROR_SERVICE_UNAVAILABLE, e);
}
}
/**
* Handle POST requests: provision a new application.
*/
@Override
public void acceptRepresentation(Representation entity) throws ResourceException {
super.acceptRepresentation(entity); //To change body of overridden methods use File | Settings | File Templates.
try {
StorageEngine storageEngine = storageManager.getPreferredStorageEngine();
if (MediaType.MULTIPART_ALL.equals(entity.getMediaType(), true)
|| MediaType.MULTIPART_FORM_DATA.equals(entity.getMediaType(), true)) {
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setSizeThreshold(1000240);
RestletFileUpload upload = new RestletFileUpload(factory);
List<FileItem> files = upload.parseRequest(getRequest());
logger.log(Level.INFO, "Found {0} items", files.size());
for (FileItem fi : files) {
if ("oar".equals(fi.getFieldName())) {
// download it as a temp file
File file = File.createTempFile("elastic-grid", "oar");
fi.write(file);
// upload it to storage container
logger.log(Level.INFO, "Uploading OAR ''{0}'' to {1}'s container ''{2}''",
new Object[]{fi.getName(), storageEngine.getStorageName(), dropBucket});
Container container = storageEngine.findContainerByName(dropBucket);
try {
container.uploadStorable(file);
} catch (StorageException e) {
logger.log(Level.SEVERE,
String.format("Could not upload OAR '%s' to %s's container '%s'",
fi.getName(), storageEngine.getStorageName(), dropBucket), e);
throw new ResourceException(Status.SERVER_ERROR_INSUFFICIENT_STORAGE, e);
}
}
}
// Set the status of the response.
logger.info("Redirecting to " + getRequest().getOriginalRef());
getResponse().setLocationRef(getRequest().getOriginalRef().addSegment("??")); // todo: figure out the proper URL
} catch (FileUploadException e) {
e.printStackTrace();
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e);
}
} else if (new MediaType("application/oar").equals(entity.getMediaType())) {
try {
// extract filename information
Form form = (Form) getRequest().getAttributes().get("org.restlet.http.headers");
// upload it to storage container
String fileName = form.getFirstValue("x-filename");
logger.log(Level.INFO, "Uploading OAR ''{0}'' to {1}'s container ''{2}''",
new Object[]{fileName, storageEngine.getStorageName(), dropBucket});
Container container = storageEngine.findContainerByName(dropBucket);
try {
container.uploadStorable(fileName, entity.getStream(), "application/oar");
} catch (StorageException e) {
logger.log(Level.SEVERE,
String.format("Could not upload OAR '%s' to %s's container '%s'",
fileName, storageEngine.getStorageName(), dropBucket), e);
throw new ResourceException(Status.SERVER_ERROR_INSUFFICIENT_STORAGE, e);
}
// Set the status of the response
logger.info("Redirecting to " + getRequest().getOriginalRef());
getResponse().setLocationRef(getRequest().getOriginalRef().addSegment("??")); // todo: figure out the proper URL
} catch (Exception e) {
e.printStackTrace();
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e);
}
} else {
throw new ResourceException(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE);
}
} catch (Exception e) {
e.printStackTrace();
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, e);
}
}
@Override
protected void describeGet(MethodInfo info) {
super.describeGet(info);
info.setDocumentation("Describe all applications running on the cluster {clusterName}.");
info.getResponse().setDocumentation("The cluster.");
RepresentationInfo representation = new RepresentationInfo();
representation.setDocumentation("This resource exposes applications running on cluster {clusterName}.");
representation.getDocumentations().get(0).setTitle("applications");
representation.setMediaType(MediaType.APPLICATION_XML);
representation.getDocumentations().addAll(Arrays.asList(
new DocumentationInfo("Example of output:<pre><![CDATA[" +
"<applications xmlns=\"urn:elastic-grid:eg\">\n" +
" <application name=\"myapp\" cluster=\"cluster2\">\n" +
" <service name=\"My First Service\">\n" +
" <provisioning planned=\"1\" deployed=\"1\" pending=\"0\"/>\n" +
" </service>\n" +
" <service name=\"My Second Service\">\n" +
" <provisioning planned=\"1\" deployed=\"1\" pending=\"0\"/>\n" +
" </service>\n" +
" </application>\n" +
"</applications>" +
"]]></pre>")
));
representation.setXmlElement("eg:applications");
info.getResponse().setRepresentations(Arrays.asList(representation));
}
@Override
protected void describePost(MethodInfo info) {
super.describePost(info);
info.setDocumentation("Provision a new application on {clusterName}.");
info.getRequest().setDocumentation("The application to provision packaged as an OAR.");
RepresentationInfo formRepresentation = new RepresentationInfo();
formRepresentation.setDocumentation("HTML form with file uploads.");
formRepresentation.setMediaType(MediaType.MULTIPART_FORM_DATA);
info.getRequest().setRepresentations(Arrays.asList(formRepresentation));
}
@Override
public boolean allowPut() {
return false;
}
@Override
public boolean allowDelete() {
return false;
}
}