Mercurial Hosting > nabble
view src/nabble/model/SiteImpl.java @ 35:5ea557eece1f
remove site.isNew()
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 07 Jul 2020 09:57:53 -0600 |
parents | b0e75dfe1853 |
children | b5d56f522ea3 |
line wrap: on
line source
package nabble.model; import fschmidt.db.DbDatabase; import fschmidt.db.DbObjectFactory; import fschmidt.db.DbRecord; import fschmidt.db.DbTable; import fschmidt.db.DbUtils; import fschmidt.db.Listener; import fschmidt.db.ListenerList; import fschmidt.db.LongKey; import fschmidt.db.NoKey; import fschmidt.db.NoKeySetter; import fschmidt.util.java.CollectionUtils; import fschmidt.util.java.Computable; import fschmidt.util.java.FutureValue; import fschmidt.util.java.Memoizer; import fschmidt.util.java.SimpleCache; import jdbcpgbackup.DataFilter; import jdbcpgbackup.ZipBackup; import nabble.model.export.NodeData; import nabble.modules.ModuleManager; import nabble.naml.compiler.CompileException; import nabble.naml.compiler.Module; import nabble.naml.compiler.Program; import nabble.naml.compiler.StackTraceElement; import nabble.naml.compiler.Template; import nabble.naml.compiler.TemplatePrintWriter; import nabble.naml.namespaces.BasicNamespace; import nabble.view.web.template.NabbleNamespace; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.StringWriter; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; final class SiteImpl implements Site { private static final Logger logger = LoggerFactory.getLogger(SiteImpl.class); final SiteKey siteKey; private final DbRecord<NoKey,SiteImpl> record; private long rootNodeId; private NodeImpl rootNode = null; private final Object tweakLock = new Object(); private CompileException tweakException = null; private Program program = null; private final Date whenCreated; SiteImpl(SiteKey siteKey) { this.siteKey = siteKey; record = table(siteKey).newRecord(this); record.fields().put( "root_node_id", 0L ); whenCreated = new Date(); } void setRoot(NodeImpl node) { if( !node.isInDb() ) throw new RuntimeException("node must be in db"); this.rootNodeId = node.getId(); record.fields().put( "root_node_id", rootNodeId ); this.rootNode = node; record.update(); } private SiteImpl(SiteKey siteKey,NoKey key,ResultSet rs) throws SQLException { this.siteKey = siteKey; record = table(siteKey).newRecord(this,key); rootNodeId = rs.getLong("root_node_id"); whenCreated = rs.getTimestamp("when_created"); for( ExtensionFactory<Site,?> factory : extensionFactories ) { Object obj = factory.construct(this,rs); if( obj != null ) getExtensionMap().put(factory,obj); } } public DbRecord<NoKey,SiteImpl> getDbRecord() { return record; } private DbTable<NoKey,SiteImpl> table() { return record.getDbTable(); } private DbDatabase db() { return table().getDbDatabase(); } public DbDatabase getDb() { return siteKey.getDb(); } public long getId() { return siteKey.getId(); } /* private void calcBaseUrl() { siteGlobal().calcBaseUrl(); } */ long getRootNodeId() { return rootNodeId; } NodeImpl getRootNodeImpl() { if( DbUtils.isStale(rootNode) ) { rootNode = NodeImpl.getNode(siteKey,rootNodeId); } return rootNode; } public Node getRootNode() { return getRootNodeImpl(); } public Date getWhenCreated() { return whenCreated; } /** To be called from the shell */ public void setWhenCreated(int day, int month, int year) { Calendar cal = Calendar.getInstance(); cal.set(Calendar.DAY_OF_MONTH, day); cal.set(Calendar.MONTH, month); cal.set(Calendar.YEAR, year); DbRecord<NoKey,?> record = getDbRecord(); record.fields().put("when_created", cal.getTime()); record.update(); } SiteGlobal siteGlobal() { return siteKey.siteGlobal(); } @Override public boolean equals(Object obj) { return this==obj || obj instanceof SiteImpl && record.isInDb() && ((SiteImpl)obj).getId()==getId(); } @Override public int hashCode() { return (int)getId(); } public Program getProgram() { synchronized(tweakLock) { if( program == null ) { if(trace()) logger.error("getting Program for "+this+" "+System.identityHashCode(this)+" tweakException="+tweakException); List<Module> modules = ModuleManager.getModules(SiteImpl.this); program = Program.getInstance(modules); } return program; } } public Template getTemplate(String templateName,Class... base) { synchronized(tweakLock) { try { return getProgram().getTemplate(templateName,base); } catch(CompileException e) { if( setTweakException(e) ) { return getTemplate(templateName,base); } throw new RuntimeException(""+this+" "+System.identityHashCode(this),e); } } } public void setCustomDomain(String customDomain) { siteGlobal().setCustomDomain(customDomain); } public String getCustomDomain() { return siteGlobal().getCustomDomain(); } public String getBaseUrl() { return siteGlobal().getBaseUrl(); } List<UserImpl> getPosters() { try { Connection con = db().getConnection(); PreparedStatement stmt = con.prepareStatement( "select * from user_ where user_id in (" +"select distinct owner_id from node where redirect is null" +")" ); try { return UserImpl.getUsers(siteKey,stmt); } finally { stmt.close(); con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } public List<User> getUsers(String cnd) { List<User> list = new ArrayList<User>(); getUserImpls(list, cnd); return list; } List<UserImpl> getUserImpls(String cnd) { List<UserImpl> list = new ArrayList<UserImpl>(); getUserImpls(list, cnd); return list; } void getUserImpls(List<? super UserImpl> list, String cnd) { try { Connection con = db().getConnection(); PreparedStatement stmt = con.prepareStatement( "select * from user_" + (cnd == null? "" : " where " + cnd) ); try { UserImpl.getUsers(siteKey,stmt,list); } finally { stmt.close(); con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } public int getActivity() { return siteGlobal().getActivity(); } public void setActivity(int activity) { siteGlobal().setActivity(activity); } public boolean isEmbarrassing() { return siteGlobal().isEmbarrassing(); } public void setEmbarrassing(boolean isEmbarrassing) { siteGlobal().setEmbarrassing(isEmbarrassing); } public String toString() { return "site-"+getId(); } static final ListenerList<SiteImpl> postUpdateListeners = new ListenerList<SiteImpl>(); private static final ListenerList<SiteImpl> postDeleteListeners = new ListenerList<SiteImpl>(); private static final ListenerList<SiteImpl> preInsertListeners = new ListenerList<SiteImpl>(); private static final ListenerList<SiteImpl> preUpdateListeners = new ListenerList<SiteImpl>(); private static Computable<SiteKey,DbTable<NoKey,SiteImpl>> tables = new SimpleCache<SiteKey,DbTable<NoKey,SiteImpl>>(new WeakHashMap<SiteKey,DbTable<NoKey,SiteImpl>>(), new Computable<SiteKey,DbTable<NoKey,SiteImpl>>() { public DbTable<NoKey,SiteImpl> get(SiteKey siteKey) { DbDatabase db = siteKey.getDb(); final long siteId = siteKey.getId(); DbTable<NoKey,SiteImpl> table = db.newTable("site",NoKeySetter.INSTANCE , new DbObjectFactory<NoKey,SiteImpl>() { public SiteImpl makeDbObject(NoKey key,ResultSet rs,String tableName) throws SQLException { SiteKey siteKey = SiteKey.getInstance(siteId); return new SiteImpl(siteKey,key,rs); } } ); table.getPreInsertListeners().add(preInsertListeners); table.getPreUpdateListeners().add(preUpdateListeners); table.getPostUpdateListeners().add(postUpdateListeners); table.getPostDeleteListeners().add(postDeleteListeners); return table; } }); private static DbTable<NoKey,SiteImpl> table(SiteKey siteKey) { return tables.get(siteKey); } static SiteImpl getSite(SiteKey siteKey,long siteId) { return table(siteKey).findByPrimaryKey(NoKey.INSTANCE); } static SiteImpl getSite(SiteKey siteKey,ResultSet rs) throws SQLException { return table(siteKey).getDbObject(rs); } public Date getDeleteDate() { return siteGlobal().getDeleteDate(); } public void clearDeleteDate() { siteGlobal().clearDeleteDate(); } static void addChangeListener(final Listener<? super SiteImpl> listener) { postUpdateListeners.add(listener); postDeleteListeners.add(listener); } static void addPreChangeListener(final Listener<? super SiteImpl> listener) { preInsertListeners.add(listener); preUpdateListeners.add(listener); } public User getUser(long id) { return getUserImpl(id); } UserImpl getUserImpl(long id) { return UserImpl.getUser(siteKey,id); } public User getUserFromEmail(String email) { return getUserImplFromEmail(email); } UserImpl getUserImplFromEmail(String email) { return UserImpl.getUserFromEmail(this,email); } public User getUserFromName(String name) { return getUserImplFromName(name); } UserImpl getUserImplFromName(String name) { return UserImpl.getUserFromName(this,name); } public User getOrCreateUnregisteredUser(String email,String name) throws ModelException { return UserImpl.getOrCreateUnregisteredUser(this,email,name); } public User getOrCreateUser(String email) { return UserImpl.getOrCreateUser(this,email); } public User getOrCreateUser(String email,String name) { UserImpl user = getUserImplFromEmail(email); if( user==null ) { user = UserImpl.createGhost(this,email); user.setNameLike(name,false); user.insert(); } return user; } public String newRegistration(String email,String password,String name,String nextUrl) throws ModelException { return UserImpl.createUser(this,email,password,name).newRegistration(nextUrl); } public User getRegistration(String registrationKey) throws ModelException { return UserImpl.getRegistration(this,registrationKey); } public List<User> getUsersByNodeCount(int i, int n, String cnd) { try { List<User> list = new ArrayList<User>(); Connection con = db().getConnection(); PreparedStatement stmt1 = con.prepareStatement( "select *, (select count(*) from node where user_.user_id=node.owner_id) as n" +" from user_" + (cnd == null? "" : " where " + cnd) +" order by n desc" +" limit ? offset ?" ); stmt1.setInt(1,n); stmt1.setInt(2,i); ResultSet rs1 = stmt1.executeQuery(); while( rs1.next() ) { UserImpl user = UserImpl.getUser(siteKey,rs1); user.setNodeCount( rs1.getInt("n") ); list.add(user); } rs1.close(); stmt1.close(); con.close(); return list; } catch(SQLException e) { throw new RuntimeException(e); } } public int getUserCount(String cnd) { try { Connection con = db().getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select count(*) as n from user_" + (cnd == null? "" : " where " + cnd) ); rs.next(); int n = rs.getInt("n"); rs.close(); stmt.close(); con.close(); return n; } catch(SQLException e) { throw new RuntimeException(e); } } public void deleteRootNode() throws ModelException { if( !db().isInTransaction() ) { db().beginTransaction(); try { SiteImpl site = DbUtils.getGoodCopy(this); site.deleteRootNode(); db().commitTransaction(); } finally { db().endTransaction(); } return; } NodeImpl oldRoot = getRootNodeImpl(); List<NodeImpl> children = oldRoot.getChildrenImpl(null).asList(); if( children.size() != 1 ) throw ModelException.newInstance("cant_delete_root","Root node must have exactly one child"); NodeImpl child = children.get(0); child.makeRoot(); setRoot(child); oldRoot.getDbRecord().delete(); } public Person getAnonymous(String cookie, String name) { return new Anonymous(this,cookie,name); } public Person getPerson(String id) { int i = id.indexOf(Anonymous.SEPERATOR); if( i == -1 ) return getUser(Long.parseLong(id)); String cookie = id.substring(0,i); String name = id.substring(i+1); if( name.length() == 0 ) name = null; return getAnonymous(cookie,name); } public void addTag(Node node,User user,String label) { TagImpl.addTag(this,node,user,label); uncacheTags(node,user); } public void deleteTags(String sqlCondition) { TagImpl.deleteTags( siteKey, sqlCondition ); } private static String tagSql(Node node,User user,String sqlCondition) { StringBuilder sb = new StringBuilder(); if( node == null ) sb.append( "node_id is null" ); else sb.append( "node_id=" ).append( node.getId() ); sb.append( " and " ); if( user == null ) sb.append( "user_id is null" ); else sb.append( "user_id=" ).append( user.getId() ); sb.append( " and " ).append( sqlCondition ); return sb.toString(); } public void deleteTags(Node node,User user,String sqlCondition) { deleteTags( tagSql(node,user,sqlCondition) ); uncacheTags(node,user); } private final Memoizer<String,Boolean> tagCache = new Memoizer<String,Boolean>(new Computable<String,Boolean>() { public Boolean get(String sqlCondition) { return TagImpl.countTags(siteKey,sqlCondition) > 0; } }); private Memoizer<String,Boolean> tagCache(Node node,User user) { if( user != null ) return ((UserImpl)user).tagCache; else if( node != null ) return ((NodeImpl)node).tagCache; else return tagCache; } private void uncacheTags(Node node,User user) { if( user != null ) DbUtils.uncache((UserImpl)user); else if( node != null ) DbUtils.uncache((NodeImpl)node); else DbUtils.uncache(this); } public boolean hasTags(Node node,User user,String sqlCondition) { return tagCache(node,user).get( tagSql(node,user,sqlCondition) ); } public int countTags(String sqlCondition) { return TagImpl.countTags(siteKey,sqlCondition); } public List<String> findTagLabels(String sqlCondition) { return TagImpl.findTagLabels(this,sqlCondition); } public List<User> findTagUsers(String sqlCondition) { return new ArrayList<User>( UserImpl.getUsers( siteKey, TagImpl.findTagUserIds(this,sqlCondition) ) ); } public List<Long> findTagUserIds(String sqlCondition) { return TagImpl.findTagUserIds(this,sqlCondition); } public List<Node> findTagNodes(String sqlCondition) { return new ArrayList<Node>( NodeImpl.getNodes( siteKey, TagImpl.findTagNodeIds(this,sqlCondition) ) ); } public List<Long> findTagNodeIds(String sqlCondition) { return TagImpl.findTagNodeIds(this,sqlCondition); } private FutureValue<Map<String,Boolean>> modulesEnabled = new FutureValue<Map<String,Boolean>>() { protected Map<String,Boolean> compute() { Map<String,Boolean> map = new HashMap<String,Boolean>(); try { Connection con = db().getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select module_name, is_enabled from module" ); while( rs.next() ) { String moduleName = rs.getString("module_name"); boolean isEnabled = rs.getBoolean("is_enabled"); map.put(moduleName,isEnabled); } rs.close(); stmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } if( map.isEmpty() ) map = Collections.emptyMap(); return map; } }; public boolean isModuleEnabled(String moduleName) { Boolean b = modulesEnabled.get().get(moduleName); return b != null ? b : ModuleManager.isEnabledByDefault(moduleName); } public void setModuleEnabled(String moduleName,boolean isEnabled) { try { Connection con = db().getConnection(); if( isEnabled == ModuleManager.isEnabledByDefault(moduleName) ) { PreparedStatement stmt = con.prepareStatement( "delete from module where module_name = ?" ); stmt.setString( 1, moduleName ); stmt.executeUpdate(); stmt.close(); } else if( modulesEnabled.get().get(moduleName) == null ) { PreparedStatement stmt = con.prepareStatement( "insert into module (module_name,is_enabled) values (?,?)" ); stmt.setString( 1, moduleName ); stmt.setBoolean( 2, isEnabled ); stmt.executeUpdate(); stmt.close(); } else { PreparedStatement stmt = con.prepareStatement( "update module set is_enabled = ? where module_name = ?" ); stmt.setBoolean( 1, isEnabled ); stmt.setString( 2, moduleName ); stmt.executeUpdate(); stmt.close(); } con.close(); } catch(SQLException e) { throw new RuntimeException(e); } record.update(); // uncache and fire update listeners } private FutureValue<String> config = new FutureValue<String>() { protected String compute() { StringBuilder tweak = new StringBuilder(); final Set<String> names = new HashSet<String>(); try { Connection con = db().getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select name, naml from configuration" ); while( rs.next() ) { names.add( rs.getString("name") ); tweak.append(rs.getString("naml")); tweak.append("\n\n"); } rs.close(); stmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } if( !names.isEmpty() ) { Executors.executeSometime(new Runnable(){ public void run() { boolean didDelete = false; for( String name : names ) { if( !isValidConfiguration(name) ) { deleteConfiguration(name); didDelete = true; logger.error("deleted invalid config: "+name); } } if( didDelete ) update(); } }); } return tweak.toString(); } }; public String getConfigurationTweak() { return config.get(); } private volatile FutureValue<Map<String,String>> tweaks = newTweaks(); private FutureValue<Map<String,String>> newTweaks() { return new FutureValue<Map<String,String>>() { protected Map<String,String> compute() { Map<String,String> map = new HashMap<String,String>(); try { Connection con = db().getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select tweak_name, content from tweak" ); while( rs.next() ) { String tweakName = rs.getString("tweak_name"); String content = rs.getString("content"); map.put(tweakName,content); } rs.close(); stmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } return CollectionUtils.optimizeMap(map); } }; } public Map<String,String> getCustomTweaks() { return tweaks.get(); } public void setCustomTweaks(Map<String,String> tweaks) { try { Connection con = db().getConnection(); try { { Statement stmt = con.createStatement(); stmt.executeUpdate( "delete from tweak" ); stmt.close(); } { PreparedStatement stmt = con.prepareStatement( "insert into tweak (tweak_name,content) values (?,?)" ); for( Map.Entry<String,String> entry : tweaks.entrySet() ) { String tweakName = entry.getKey(); String content = entry.getValue(); stmt.setString( 1, tweakName ); stmt.setString( 2, content ); stmt.executeUpdate(); } stmt.close(); } } finally { con.close(); } DailyNumber.tweaks.inc(); } catch(SQLException e) { throw new RuntimeException(e); } this.tweaks = newTweaks(); synchronized(tweakLock) { this.tweakException = null; this.program = null; } record.update(); // uncache and fire update listeners } public void resetCustomTweaks() { setCustomTweaks(Collections.<String,String>emptyMap()); } private static long traceSiteId = Init.get("traceSiteId",0L); private boolean trace() { return getId() == traceSiteId; } public boolean setTweakException(CompileException tweakException) { synchronized(tweakLock) { if( this.tweakException != null ) { if(trace()) logger.error("this.tweakException already set in "+this+" "+System.identityHashCode(this),new Exception(this.tweakException)); return false; } for( StackTraceElement ste : tweakException.stackTrace ) { if( ModuleManager.isConfigurationTweak(ste.source) || ModuleManager.isCustomTweak(ste.source) ) { logger.debug("tweak exception in "+this,tweakException); if(trace()) logger.error("tweak exception in "+this+" "+System.identityHashCode(this),tweakException); this.tweakException = tweakException; this.program = null; return true; } } if(trace()) logger.error("no tweak in stack trace"); return false; } } public CompileException getTweakException() { synchronized(tweakLock) { return tweakException; } } public void update() { record.update(); } public Site getGoodCopy() { return DbUtils.getGoodCopy(this); } NodeImpl getNodeImpl(long id) { return NodeImpl.getNode(siteKey,id); } public Node getNode(long id) { return getNodeImpl(id); } public Node getNode(ResultSet rs) throws SQLException { return NodeImpl.getNode(siteKey,rs); } private void check(Collection<? extends Node> nodes) { for( Iterator<? extends Node> i = nodes.iterator(); i.hasNext(); ) { if( !i.next().getSite().equals(this) ) throw new RuntimeException("node from wrong site"); } } public Collection<? extends Node> getNodes(Collection<Long> ids) { Collection<NodeImpl> nodes = NodeImpl.getNodes(siteKey,ids); check(nodes); return nodes; } public NodeIterator<? extends Node> getNodeIterator(String sql,DbParamSetter paramSetter) { return new CursorNodeIterator( siteKey, sql, paramSetter ); } private final Memoizer<String,String> propertyCache = new Memoizer<String,String>(new Computable<String,String>() { public String get(String key) { try { Connection con = db().getConnection(); PreparedStatement stmt = con.prepareStatement( "select value from site_property where key = ?" ); stmt.setString( 1, key ); ResultSet rs = stmt.executeQuery(); try { return rs.next() ? rs.getString("value") : null; } finally { rs.close(); stmt.close(); con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } }); public String getProperty(String key) { return propertyCache.get(key); } public void setProperty(String key,String value) { try { Connection con = db().getConnection(); PreparedStatement stmt = con.prepareStatement( "delete from site_property where key = ?" ); stmt.setString( 1, key ); stmt.executeUpdate(); stmt.close(); if( value != null ) { stmt = con.prepareStatement( "insert into site_property (key,value) values (?,?)" ); stmt.setString( 1, key ); stmt.setString( 2, value ); stmt.executeUpdate(); stmt.close(); } con.close(); } catch(SQLException e) { throw new RuntimeException(e); } finally { propertyCache.remove(key); } } public boolean isValidConfiguration(String name) { Template template = getTemplate( "is_valid_configuration", BasicNamespace.class, NabbleNamespace.class ); StringWriter sw = new StringWriter(); template.run( new TemplatePrintWriter(sw), Collections.<String,Object>singletonMap("config",name), new BasicNamespace(template), new NabbleNamespace(this) ); return Template.booleanValue(sw.toString().trim()); } private void deleteConfiguration(Connection con,String name) throws SQLException { PreparedStatement stmt = con.prepareStatement( "delete from configuration where name = ?" ); stmt.setString( 1, name ); stmt.executeUpdate(); stmt.close(); } public void deleteConfiguration(String name) { try { Connection con = db().getConnection(); deleteConfiguration(con,name); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } } public void saveConfiguration(String name,String value,String naml) { if( !isValidConfiguration(name) ) throw new RuntimeException("invalid configuration: "+name); try { Connection con = db().getConnection(); deleteConfiguration(con,name); PreparedStatement stmt = con.prepareStatement( "insert into configuration (name,value,naml) values (?,?,?)" ); stmt.setString( 1, name ); stmt.setString( 2, value ); stmt.setString( 3, naml ); stmt.executeUpdate(); stmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } } public String getConfigurationValue(String name) { if( !isValidConfiguration(name) ) throw new RuntimeException("invalid configuration: "+name); try { Connection con = db().getConnection(); PreparedStatement stmt = con.prepareStatement( "select value from configuration where name = ?" ); stmt.setString( 1, name ); ResultSet rs = stmt.executeQuery(); try { return rs.next() ? rs.getString("value") : null; } finally { rs.close(); stmt.close(); con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } public String getNextUrl(String registrationKey) { return UserImpl.getNextUrl(siteKey,registrationKey); } public Node newNode(NodeData data) throws ModelException { return new NodeImpl(this,data); } public Collection<Node> cacheLastNodes(Collection<Node> nodes) { Collection<LongKey> keys = new ArrayList<LongKey>(); for( Node n : nodes ) { NodeImpl node = (NodeImpl)n; keys.add( new LongKey(node.getLastNodeId()) ); } Map<LongKey,NodeImpl> objs = NodeImpl.table(siteKey).findByPrimaryKey(keys); return new ArrayList<Node>(objs.values()); } public void addTask(String task) { siteKey.addTask(task); } private Map<ExtensionFactory<Site,?>,Object> extensionMap; private synchronized Map<ExtensionFactory<Site, ?>, Object> getExtensionMap() { if (extensionMap == null) extensionMap = new HashMap<ExtensionFactory<Site, ?>, Object>(); return extensionMap; } public <T> T getExtension(ExtensionFactory<Site,T> factory) { synchronized(getExtensionMap()) { Object obj = extensionMap.get(factory); if( obj == null ) { obj = factory.construct(this); if( obj != null ) extensionMap.put(factory,obj); } return factory.extensionClass().cast(obj); } } private static Collection<ExtensionFactory<Site,?>> extensionFactories = new CopyOnWriteArrayList<ExtensionFactory<Site,?>>(); static <T> void addExtensionFactory(ExtensionFactory<Site,T> factory) { extensionFactories.add(factory); Db.clearCache(); } public Site getSite() { return this; } public long getSourceId() { throw new UnsupportedOperationException(); } public Message.SourceType getMessageSourceType() { return Message.SourceType.SITE; } public void delete() { if( getRootNode().getOwner() instanceof User ) { File file = backup(); // Don't sent the deletion email if the site has only one node if (getRootNode().getDescendantCount() > 1) { Template template = getTemplate( "site deletion email", BasicNamespace.class, NabbleNamespace.class ); Map<String,Object> params = new HashMap<String,Object>(); params.put("file",file.getName()); template.run( TemplatePrintWriter.NULL, params, new BasicNamespace(template), new NabbleNamespace(this) ); } } kill(); } public void kill() { if( !db().isInTransaction() ) { db().beginTransaction(); try { SiteImpl site = DbUtils.getGoodCopy(SiteImpl.this); site.kill(); db().commitTransaction(); } finally { db().endTransaction(); } return; } SiteGlobal siteGlobal = siteGlobal(); if( siteGlobal == null ) throw new NullPointerException("siteGlobal not found for "+siteKey); try { Connection con = Db.dbPostgres().getConnection(); Statement stmt = con.createStatement(); stmt.executeUpdate( "drop schema " + siteKey.schema() + " cascade" ); DbUtils.uncache(this); stmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } siteGlobal.getDbRecord().delete(); } private static final String SALT = Init.get("salt","zDf3s"); private static final File schemaDir = new File((String)Init.get("home_dir")+"local/schemas/"); private static File getBackupFile(long siteId) { int hash = Math.abs(( SALT + siteId ).hashCode()); String filename = "site_"+siteId+"_"+hash+".zip"; return new File(schemaDir,filename); } public File backup() { File file = getBackupFile(getId()); backup(file); return file; } public void backup(String filename) { backup( new File(filename) ); } private void backup(File file) { file.delete(); ZipBackup backup = new ZipBackup( file, Db.completeUrl ); backup.dump( Collections.singleton(siteKey.schema()), DataFilter.ALL_DATA ); } static final DataFilter SCHEMA_DATA = new DataFilter() { public boolean dumpData(String schema,String tableName) { return tableName.equals("version"); } }; public void backupSchema(String filename) { ZipBackup backup = new ZipBackup( new File(filename), Db.completeUrl ); backup.dump( Collections.singleton(siteKey.schema()), SCHEMA_DATA ); } // in beanshell, I do: // s = ModelHome.getSite(2) // s.backupSchema("/Users/Franklin/hg/nabble/src/nabble/data/site.schema") }