Mercurial Hosting > nabble
view src/nabble/model/export/Export.java @ 62:4674ed7d56df default tip
remove n2
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sat, 30 Sep 2023 20:25:29 -0600 |
parents | 72765b66e2c3 |
children |
line wrap: on
line source
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.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 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); // 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(); 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); } // 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(); } } 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(); } }