/*
* DatabaseListResource.java
*
* Created on November 23, 2007, 4:03 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/
package org.atomojo.app.admin;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.atomojo.app.App;
import org.atomojo.app.Storage;
import org.atomojo.app.StorageFactory;
import org.atomojo.app.auth.User;
import org.atomojo.app.client.XMLRepresentationParser;
import org.atomojo.app.db.DB;
import org.atomojo.app.db.DBInfo;
import org.atomojo.app.db.RemoteApp;
import org.atomojo.app.db.SyncProcess;
import org.atomojo.app.db.SyncTarget;
import org.atomojo.app.sync.PullSynchronizer;
import org.atomojo.app.sync.SyncException;
import org.infoset.xml.Document;
import org.infoset.xml.Element;
import org.infoset.xml.XMLException;
import org.infoset.xml.util.DocumentDestination;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ServerResource;
/**
*
* @author alex
*/
public class AllDatabaseRestoreResource extends ServerResource
{
XMLRepresentationParser parser = new XMLRepresentationParser();
/** Creates a new instance of SyncResource */
public AllDatabaseRestoreResource() {
setNegotiated(false);
}
public Representation get()
{
return null;
}
public Representation post(Representation entity)
{
if (!XMLRepresentationParser.isXML(entity.getMediaType())) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Non-XML media type for entity body: "+entity.getMediaType().getName());
}
Map<String,DBInfo> dbList = (Map<String,DBInfo>)getContext().getAttributes().get(DatabaseListResource.DB_LIST);
Map<String,DBInfo> autodbList = (Map<String,DBInfo>)getContext().getAttributes().get(DatabaseListResource.AUTO_DB_LIST);
StorageFactory storageFactory = (StorageFactory)getContext().getAttributes().get(DatabaseListResource.STORAGE_FACTORY);
try {
DocumentDestination dest = new DocumentDestination();
parser.parse(entity,AdminApplication.createAdminDocumentDestination(dest,AdminXML.NM_RESTORE));
Document doc = dest.getDocument();
Element top = doc.getDocumentElement();
String location = top.getAttributeValue("location");
if (location==null) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("The 'location' attribute is missing.");
}
location = location.trim();
if (location.length()==0) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("The 'location' attribute is empty.");
}
File dir = new File(location);
if (!dir.exists()) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("The "+dir.getAbsolutePath()+" doesn't exist.");
}
if (!dir.canRead()) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("Cannot write to "+dir.getAbsolutePath());
}
Set<String> dbNames = new TreeSet<String>();
Iterator<Element> names = top.getElementsByName(AdminXML.NM_NAME);
while (names.hasNext()) {
dbNames.add(names.next().getText());
}
List<Map<String,DBInfo>> lists = new ArrayList<Map<String,DBInfo>>();
lists.add(dbList);
lists.add(autodbList);
User user = (User)getRequest().getAttributes().get(App.USER_ATTR);
boolean ok = true;
List<String> failures = new ArrayList<String>();
for (Map<String,DBInfo> map : lists) {
for (DBInfo dbinfo : map.values()) {
DB db = dbinfo.getDB();
if (dbNames.size()>0 && !dbNames.contains(db.getName())) {
continue;
}
File dbDir = new File(dir,db.getName());
boolean needsCleanup = false;
try {
Storage storage = storageFactory.getStorage(db);
File zipFile = new File(dir,db.getName()+".zip");
if (!dbDir.exists() && zipFile.exists()) {
// This is what we want but it doesn't work
//introspectionURI = new URI("jar:"+zipFile.toURI()+"!/_introspection_.xml");
getLogger().info("Extracting backup for restore...");
needsCleanup = true;
extract(zipFile,zipFile.getParentFile());
}
File introspect = new File(dbDir,"_introspection_.xml");
if (!introspect.exists()) {
getLogger().info("No database backup for database "+db.getName());
continue;
}
URI introspectionURI = introspect.toURI();
RemoteApp app = new RemoteApp(db,"backup");
app.setIntrospection(introspectionURI);
app.setRoot(dbDir.toURI());
SyncTarget target = new SyncTarget(db,"");
SyncProcess proc = new SyncProcess(db,"restore",true,target,app,null);
PullSynchronizer restore = new PullSynchronizer(getLogger(),getApplication().getMetadataService(),user,db,storage,proc);
restore.setAdditive(false);
try {
restore.sync();
if (restore.getErrorCount()>0) {
ok = false;
getLogger().severe("Restore failed on "+db.getName());
failures.add(db.getName());
}
} catch (SyncException ex) {
getLogger().log(Level.SEVERE,"Restore failed on "+db.getName()+" due to exception.",ex);
ok = false;
failures.add(db.getName());
}
} catch (Exception ex) {
getLogger().log(Level.SEVERE,"Cannot restore database "+db.getName()+" due to exception.",ex);
ok = false;
failures.add(db.getName());
}
if (needsCleanup) {
delete(dbDir);
}
}
}
if (ok) {
getResponse().setStatus(Status.SUCCESS_CREATED);
return null;
} else {
StringBuilder message = new StringBuilder();
message.append("Failure on databases:");
for (String name : failures) {
message.append(" ");
message.append(name);
}
getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
return new StringRepresentation(message.toString());
}
} catch (IOException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("I/O exception: "+ex.getMessage());
} catch (XMLException ex) {
getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
return new StringRepresentation("XML exception: "+ex.getMessage());
}
}
public void extract(File zipFile, File targetDir)
throws IOException
{
byte[] buffer = new byte[16384];
ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry zipEntry = zipInput.getNextEntry();
while (zipEntry != null) {
//for each entry to be extracted
if (zipEntry.isDirectory()) {
File dir = new File(targetDir,zipEntry.getName());
getLogger().info("Creating: "+dir);
if (!dir.mkdirs()) {
throw new IOException("Cannot make directory path: "+dir);
}
} else {
File targetFile = new File(targetDir,zipEntry.getName());
if (!targetFile.getParentFile().exists()) {
getLogger().info("Creating: "+targetFile.getParentFile());
if (!targetFile.getParentFile().mkdirs()) {
throw new IOException("Cannot make directory path: "+targetFile.getParentFile());
}
}
getLogger().info("Extracting: "+targetFile);
FileOutputStream output = new FileOutputStream(targetFile);
int len = 0;
while ((len=zipInput.read(buffer,0,buffer.length))>-1) {
output.write(buffer,0,len);
}
output.flush();
output.close();
}
zipInput.closeEntry();
zipEntry = zipInput.getNextEntry();
}
zipInput.close();
}
public void delete(File file) {
final List<File> queue = new ArrayList<File>();
queue.add(file);
boolean ok = true;
int mark = -1;
while (ok && queue.size()>0) {
File target = queue.remove(queue.size()-1);
if (target.isDirectory()) {
if (mark==queue.size()) {
ok = target.delete();
mark = -1;
} else {
mark = queue.size();
queue.add(target);
target.listFiles(new FileFilter() {
public boolean accept(File f)
{
queue.add(f);
return false;
}
});
}
} else {
ok = target.delete();
}
}
}
}