/*
* ModeShape (http://www.modeshape.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modeshape.web.jcr.rest.handler;
import java.io.File;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Response;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.modeshape.common.util.StringUtil;
import org.modeshape.jcr.api.BackupOptions;
import org.modeshape.jcr.api.Problem;
import org.modeshape.jcr.api.Problems;
import org.modeshape.jcr.api.Repository;
import org.modeshape.jcr.api.RepositoryManager;
import org.modeshape.jcr.api.RestoreOptions;
import org.modeshape.web.jcr.rest.RestHelper;
import org.modeshape.web.jcr.rest.model.JSONAble;
import org.modeshape.web.jcr.rest.model.RestException;
import org.modeshape.web.jcr.rest.model.RestWorkspaces;
/**
* An handler which returns POJO-based rest model instances.
*
* @author Horia Chiorean (hchiorea@redhat.com)
*/
public final class RestRepositoryHandler extends AbstractHandler {
private static final String BACKUP_LOCATION_INIT_PARAM = "backupLocation";
private static final String JBOSS_DOMAIN_DATA_DIR = "jboss.domain.data.dir";
private static final String JBOSS_SERVER_DATA_DIR = "jboss.server.data.dir";
private static final String USER_HOME = "user.home";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("ddMMyyyy_HHmmss");
/**
* Returns the list of workspaces available to this user within the named repository.
*
* @param request the servlet request; may not be null
* @param repositoryName the name of the repository; may not be null
* @return the list of workspaces available to this user within the named repository, as a {@link RestWorkspaces} object
*
* @throws RepositoryException if there is any other error accessing the list of available workspaces for the repository
*/
public RestWorkspaces getWorkspaces( HttpServletRequest request,
String repositoryName ) throws RepositoryException {
assert request != null;
assert repositoryName != null;
RestWorkspaces workspaces = new RestWorkspaces();
Session session = getSession(request, repositoryName, null);
for (String workspaceName : session.getWorkspace().getAccessibleWorkspaceNames()) {
String repositoryUrl = RestHelper.urlFrom(request);
workspaces.addWorkspace(workspaceName, repositoryUrl);
}
return workspaces;
}
/**
* Performs a repository backup.
*/
public Response backupRepository( ServletContext context,
HttpServletRequest request,
String repositoryName,
BackupOptions options ) throws RepositoryException {
final File backupLocation = resolveBackupLocation(context);
Session session = getSession(request, repositoryName, null);
String repositoryVersion = session.getRepository().getDescriptorValue(Repository.REP_VERSION_DESC).getString().replaceAll("\\.","");
final String backupName = "modeshape_" + repositoryVersion + "_" + repositoryName + "_backup_" + DATE_FORMAT.format(new Date());
final File backup = new File(backupLocation, backupName);
if (!backup.mkdirs()) {
throw new RuntimeException("Cannot create backup folder: " + backup);
}
logger.debug("Backing up repository '{0}' to '{1}', using '{2}'", repositoryName, backup, options);
RepositoryManager repositoryManager = ((org.modeshape.jcr.api.Workspace)session.getWorkspace()).getRepositoryManager();
repositoryManager.backupRepository(backup, options);
final String backupURL;
try {
backupURL = backup.toURI().toURL().toString();
} catch (MalformedURLException e) {
//should never happen
throw new RuntimeException(e);
}
JSONAble responseContent = new JSONAble() {
@Override
public JSONObject toJSON() throws JSONException {
JSONObject object = new JSONObject();
object.put("name", backupName);
object.put("url", backupURL);
return object;
}
};
return Response.status(Response.Status.CREATED).entity(responseContent).build();
}
/**
* Restores a repository using an existing backup.
*/
public Response restoreRepository( ServletContext context,
HttpServletRequest request,
String repositoryName,
String backupName,
RestoreOptions options ) throws RepositoryException {
if (StringUtil.isBlank(backupName)) {
throw new IllegalArgumentException("The name of the backup cannot be null");
}
File backup = resolveBackup(context, backupName);
logger.debug("Restoring repository '{0}' from backup '{1}' using '{2}'", repositoryName, backup, options);
Session session = getSession(request, repositoryName, null);
RepositoryManager repositoryManager = ((org.modeshape.jcr.api.Workspace)session.getWorkspace()).getRepositoryManager();
final Problems problems = repositoryManager.restoreRepository(backup, options);
if (!problems.hasProblems()) {
return Response.ok().build();
}
List<JSONAble> response = new ArrayList<JSONAble>(problems.size());
for (Problem problem : problems) {
RestException exception = problem.getThrowable() != null ?
new RestException(problem.getMessage(), problem.getThrowable()) :
new RestException(problem.getMessage());
response.add(exception);
}
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(response).build();
}
private File resolveBackup( ServletContext context, String backupName ) {
// first look at the servlet init param
String backupRoot = context.getInitParameter(BACKUP_LOCATION_INIT_PARAM);
if (!StringUtil.isBlank(backupRoot)) {
if (isValidDir(backupRoot + "/" + backupName)) {
return new File(backupRoot + "/" + backupName);
}
// try resolving it as a system property
backupRoot = System.getProperty(backupRoot);
if (isValidDir(backupRoot + "/" + backupName)) {
return new File(backupRoot + "/" + backupName);
}
}
// jboss.domain.data.dir
backupRoot = System.getProperty(JBOSS_DOMAIN_DATA_DIR);
if (isValidDir(backupRoot + "/" + backupName)) {
return new File(backupRoot + "/" + backupName);
}
// jboss.server.data.dir
backupRoot = System.getProperty(JBOSS_SERVER_DATA_DIR);
if (isValidDir(backupRoot + "/" + backupName)) {
return new File(backupRoot + "/" + backupName);
}
// finally user.home
backupRoot = System.getProperty(USER_HOME);
if (isValidDir(backupRoot + "/" + backupName)) {
return new File(backupRoot + "/" + backupName);
}
// none of the above are available, so fail
throw new IllegalArgumentException(
"Cannot locate backup '" + backupName + "' anywhere on the server in the following locations:" +
BACKUP_LOCATION_INIT_PARAM + " context param, " + JBOSS_DOMAIN_DATA_DIR + ", " + JBOSS_SERVER_DATA_DIR + ", "
+ USER_HOME);
}
private File resolveBackupLocation( ServletContext context ) {
// first look at the servlet init param 'backupLocation'
String backupLocation = context.getInitParameter(BACKUP_LOCATION_INIT_PARAM);
if (!StringUtil.isBlank(backupLocation)) {
if (isValidDir(backupLocation)) {
return new File(backupLocation);
}
// try resolving it as a system property
backupLocation = System.getProperty(backupLocation);
if (isValidDir(backupLocation)) {
return new File(backupLocation);
}
}
// jboss.domain.data.dir
backupLocation = System.getProperty(JBOSS_DOMAIN_DATA_DIR);
if (isValidDir(backupLocation)) {
return new File(backupLocation);
}
// jboss.server.data.dir
backupLocation = System.getProperty(JBOSS_SERVER_DATA_DIR);
if (isValidDir(backupLocation)) {
return new File(backupLocation);
}
// finally user.home
backupLocation = System.getProperty(USER_HOME);
if (isValidDir(backupLocation)) {
return new File(backupLocation);
}
// none of the above are available, so fail
throw new IllegalArgumentException(
"None of the following locations are writable folders on the server: " +
BACKUP_LOCATION_INIT_PARAM + " context param, " + JBOSS_DOMAIN_DATA_DIR + ", " + JBOSS_SERVER_DATA_DIR + ", "
+ USER_HOME);
}
private boolean isValidDir( String dir ) {
if (StringUtil.isBlank(dir)) {
return false;
}
File dirFile = new File(dir);
return dirFile.exists() && dirFile.canWrite() && dirFile.isDirectory();
}
}