Mercurial Hosting > nabble
diff src/nabble/model/export/Export.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children | 72765b66e2c3 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nabble/model/export/Export.java Thu Mar 21 19:15:52 2019 -0600 @@ -0,0 +1,279 @@ +package nabble.model.export; + +import fschmidt.db.DbDatabase; +import fschmidt.util.java.IoUtils; +import fschmidt.util.mail.Mail; +import fschmidt.util.mail.MailAddress; +import fschmidt.util.mail.MailHome; +import fschmidt.util.mail.PlainTextContent; +import nabble.model.MailingList; +import nabble.model.ModelException; +import nabble.model.ModelHome; +import nabble.model.Executors; +import nabble.model.Node; +import nabble.model.NodeIterator; +import nabble.model.Person; +import nabble.model.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.rmi.Naming; +import java.rmi.NotBoundException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class Export implements Runnable { + + private static final Logger logger = LoggerFactory.getLogger(Export.class); + + protected static class ExportMessages { + public String getSuccessMessage(Node node) { return "Export finished successfully."; } + public String getErrorMessage(Node node, Exception e) { return "Export error: \n" + getStackTrace(e); } + public String getPermalinkNotFoundMessage(String permalink) { return "Export couldn't start because the link you provided is not a valid Nabble application: \n" + permalink; } + public String getEmailSubject(Node node) { return "Export of node " + node.getId() + " | " + node.getSubject(); } + } + + protected static final class ShutdownException extends RuntimeException {} + + private static final Pattern URL_BASE = Pattern.compile( "^\\w+://[^/]+/"); + private static final Pattern SERVER_FORMAT = Pattern.compile( "(localhost|\\d+\\.\\d+\\.\\d+\\.\\d+):\\d+"); + private final String email; + private final Node rootNode; + private final Import imp; + private final ExportMessages messages; + private final String permalink; + private boolean handledMailingList = false; + private Set<Long> movedUserIds = new TreeSet<Long>(); + + protected Export(Node rootNode,String permalink,String email, ImportServer is, ExportMessages messages) + throws IOException + { + logger.info("Exporting node ID = " + rootNode.getId()); + setExporting(rootNode.getSite().getId(), true); + this.rootNode = rootNode; + imp = is.newImport(permalink,rootNode.getId()); + this.email = email; + this.messages = messages; + this.permalink = permalink; + } + + public Export(Node rootNode,String permalink,String email) + throws IOException + { + this(rootNode,permalink,email,getServer(permalink),new ExportMessages()); + } + + // Global Information + public static Set<Long> exportSiteIds = new HashSet<Long>(); + + private static synchronized void setExporting(long siteId, boolean start) { + if (start) { + exportSiteIds.add(siteId); + } else + exportSiteIds.remove(siteId); + } + + private static String getServerAddress(String permalink) throws IOException { + Matcher m = URL_BASE.matcher(permalink); + if( !m.find() ) + throw new IllegalArgumentException(); + String base = m.group(); + return IoUtils.readPage(base+"util/Rmi.jtp"); + } + + private static Remote lookup(String rmiServer,String name) + throws RemoteException + { + try { + return Naming.lookup("//"+rmiServer+"/"+name); + } catch(MalformedURLException e) { + throw new RuntimeException(e); + } catch(NotBoundException e) { + throw new RuntimeException(e); + } + } + + private static ImportServer getServer(String permalink) throws IOException { + return (ImportServer) lookup(getServerAddress(permalink),"import"); + } + + public static boolean isValidExportServer(String permalink) { + try { + String server = getServerAddress(permalink); + return SERVER_FORMAT.matcher(server).matches(); + } catch (IOException e) { + return false; + } catch (IllegalArgumentException e) { + return false; + } + } + + public void run() { + logger.info("Started export thread for ID = " + rootNode.getId() + " / " + email); + PlainTextContent content = null; + ModelHome.beginImport(); + long siteId = this.rootNode.getSite().getId(); + boolean closed = false; + try { + Node rootNode = this.rootNode.getGoodCopy(); + long parentId = imp.getNodeId(permalink); + export(rootNode.getGoodCopy(),parentId,null); + if( handledMailingList ) { + handledMailingList = false; + export(rootNode.getGoodCopy(),parentId,null); + if( handledMailingList ) + throw new RuntimeException(); + } + // The last step is to delete the old nodes, so there is no reason + // to keep this connection open. Let's close it. + imp.close(); + closed = true; + + // Delete the old nodes now + rootNode.deleteRecursively(); + content = new PlainTextContent(messages.getSuccessMessage(rootNode)); + } catch(ShutdownException e) { + throw e; + } catch(RuntimeException e) { + logger.error(rootNode.toString(),e); + content = new PlainTextContent(messages.getErrorMessage(rootNode, e)); + } catch(RemoteException e) { + logger.error(rootNode.toString(),e); + content = new PlainTextContent(messages.getErrorMessage(rootNode, e)); + } catch(Import.BadLink e) { + content = new PlainTextContent(messages.getPermalinkNotFoundMessage(permalink)); + } finally { + ModelHome.endImport(); + setExporting(siteId, false); + if (!closed) { + try { + imp.close(); + } catch(Exception e) { + logger.error("imp.close",e); + if (content == null) + content = new PlainTextContent(messages.getErrorMessage(rootNode, e)); + } + } + } + Mail mail = MailHome.newMail(); + MailAddress to = new MailAddress(email); + mail.setFrom(new MailAddress(ModelHome.noReply, "Nabble")); + mail.setTo(to); + mail.setContent(content); + mail.setSubject(messages.getEmailSubject(rootNode)); + ModelHome.send(mail); + } + + private void export(Node node,long parentId,int[] pin) + throws RemoteException + { + if( Executors.isShuttingDown() ) + throw new ShutdownException(); + long id = node.getExportedNodeId(); + try { + final MailingList ml = node.getMailingList(); + Integer pinOrder = pin == null || !node.isPinned()? null : ++pin[0]; + if( id==0L ) { + NodeData data = node.getData(); + data.parentId = parentId; + data.pin = pinOrder; + String redirectUrl = imp.importNode(data); + try { + id = imp.getNodeId(redirectUrl); + } catch(Import.BadLink e) { + logger.error(""+node+" url = "+redirectUrl,e); + throw new RuntimeException("redirect url not found: "+redirectUrl); + } + setExportedNodeId(node, id); + if (ml != null) { + imp.setExportOwner(id,email); + imp.subscribe(id); + ml.setExportOwner(email); + ml.update(); + ml.unsubscribe(); + handledMailingList = true; + } + } else { + if (ml!=null) { + imp.setExportOwner(id,null); + ml.setExportOwner(null); + ml.update(); + } + } + + if (ml != null) + imp.setMailingList(id); + + // Move author + Person author = node.getOwner(); + if (author instanceof User) { + User user = (User) author; + if (user.isRegistered() && !movedUserIds.contains(user.getId())) { + String smallAvatarUrl = null; + String bigAvatarUrl = null; + if (user.hasAvatar()) { + String base = node.getSite().getBaseUrl() + "/file/a" + user.getId() + '/'; + smallAvatarUrl = base + ModelHome.AVATAR_SMALL; + bigAvatarUrl = base + ModelHome.AVATAR_BIG; + } + imp.addUser(user.getName(), user.getEmail(), user.getPasswordDigest(), user.getRegistered(), smallAvatarUrl, bigAvatarUrl); + movedUserIds.add(user.getId()); + } + } + + pin = new int[1]; + + // Here we use an iterator because something may go wrong with the + // migration (e.g., the other server may go down or the network may fail) + // So we should make sure all DB connections are properly closed. + NodeIterator<? extends Node> iterator = node.getChildren(); + try { + while (iterator.hasNext()) { + export(iterator.next(), id, pin ); + } + } finally { + iterator.close(); + } + if (ml != null) + imp.setMailingList(null); + } catch(ModelException e) { + logger.error(""+node,e); + throw new RuntimeException(e.toString()); + } + } + + protected void setExportedNodeId(Node node, long id) { + final DbDatabase db = rootNode.getSite().getDb(); + db.beginTransaction(); + try { + node.getGoodCopy().setExportedNodeId(id); + db.commitTransaction(); + } finally { + db.endTransaction(); + } + } + + // Utilities -------------------------------------------------------------- + + protected static String getStackTrace(Throwable e) { + StackTraceElement[] arr = e.getStackTrace(); + StringBuilder builder = new StringBuilder(e.toString()); + builder.append('\n'); + for (StackTraceElement elem : arr) { + builder.append('\t').append(elem).append('\n'); + } + if (e.getCause() != null) { + builder.append("Caused by:\n"); + builder.append(getStackTrace(e.getCause())); + } + return builder.toString(); + } +}