Server Admin
From Mugshot Developer Wiki
Contents |
Log Files
If you deploy to the default location (~/dhdeploy) the log files include:
- dhdeploy/imbot/logs/imbot.log
- dhdeploy/jboss/log/server.log
- dhdeploy/jboss/log/noise.log
- dhdeploy/jboss/log/boot.log
- dhdeploy/mysql/log/mysqld.log
- dhdeploy/jive/logs/*.log
The most commonly-used log by far is dhdeploy/jboss/log/server.log which will tell you what the web and app server are up to. The "noise.log" has a bunch of super-verbose stuff from Hibernate and other places, you might look there if you get really stuck. "boot.log" is just JBoss starting up, not interesting most of the time.
If you're adding log statements or want to tune what's in the logs, see java logging.
Manual Database Access
To change the database directly or just run SQL queries, open a MySQL console; do this easily with:
super console mysql
There's also a "blow away database" feature:
super nuke mysql
(be careful with that)
NOTE: Hibernate will cache stuff assuming all database writes go through Hibernate, so if you change a row that's already loaded by the application server code, it won't work. In this case you have to reload JBoss after making the change, or use the console feature on /admin to make the changes instead of using SQL.
Admin Console
There is a rudimentary console/debug page located on /admin. In order to access it, you must ensure that your user is in the Administrator table:
INSERT INTO Administrator values ('<your ID>');
At the top of the page is a BeanShell console where you can execute pretty much any code you want, including access to EJBs and the persistence context (em). There are some sample commands in the box.
Following are some examples of code we've used at the admin console. See Admin Console Migrations for even more examples.
Giving more invitations to users
Currently, when new users are invited into the system by other users, they get 3 invitations they can extend. Code like this can be used in the admin console to increase invitations count. Though the invitations given to the Mugshot character should not "leak", we can exclude Mugshot from this upgrade.
me = user("marinaz@redhat.com");
mugshot = user("mugshot@mugshot.org");
gnome = user("online@gnome.org");
users = server.getEJB("PersonViewer").getAllUsers(new com.dumbhippo.server.views.UserViewpoint(me, com.dumbhippo.Site.MUGSHOT));
for (i = users.iterator(); i.hasNext();) {
user = i.next();
try {
if ((user.getAccount().getInvitations() < 10) && (user.getUser() != mugshot) && (user.getUser() != gnome)) {
user.getAccount().setInvitations(10);
}
} catch (Exception e) {
}
}
Here is the code for giving the Mugshot and Gnome Online characters invitations, which are currently used for self-invites.
mugshot = user("mugshot@mugshot.org");
mugshot.getAccount().setInvitations(1000);
gnome = user("online@gnome.org");
gnome.getAccount().setInvitations(1000);
After that, go to /invitation-admin to process invitations.
Fixing the WantsIn table
We had a bug where email addresses added to the WantsIn table were not being validated, so a number of obvious typos in the database such as "foo,bar@gmail.com" (should have been "foo.bar@gmail.com"). This later caused exceptions when we tried to send email to them.
In order to find the invalid e-mails in the database, we used the following code in the admin console:
wiList = server.getEJB("WantsInSystem").getWantsInWithoutInvites(6000);
for (int i = 0; i < wiList.size(); i++) {
address = wiList.get(i).getAddress();
try {
new javax.mail.internet.InternetAddress(address);
} catch (javax.mail.internet.AddressException e) {
out.write(address + "\n");
}
}
In order to fix them up, we manually executed the code below for each one of them:
initialAddress = "john.doe@example,com";
fixedAddress = "john.doe@example.com";
wantsIn = em.createQuery("FROM WantsIn where address=:address")
.setParameter("address",initialAddress).getSingleResult();
out.write(wantsIn.getAddress() + "\n");
try {
wantsIn2 = em.createQuery("FROM WantsIn where address=:address")
.setParameter("address", fixedAddress).getSingleResult();
out.write(wantsIn2.getAddress() + "\n");
em.remove(wantsIn);
} catch (javax.persistence.EntityNotFoundException e) {
out.write("not found\n");
wantsIn.setAddress(fixedAddress);
}
Making someone a member of a group
If a group has accumulated many followers, but the group members seem to no longer be active, it might be appropriate to make one of the followers a group member, on their request. Use the code like in the example below, note that the member_id is the person's Account id.
groupMember = em.createQuery("FROM GroupMember WHERE group_id='flPLjXFVV6CZYy'
AND member_id='wnNsJp0NCtpY2K'").getSingleResult();
groupMember.setStatus(com.dumbhippo.persistence.MembershipStatus.ACTIVE);
Priming the server with LiveUser/LivePost
We use this after starting the server to regenerate all of the LiveUser/LivePost caches:
accts = ejb("AccountSystem").getRecentlyActiveAccounts().iterator();
count = 0;
runner = ejb("TransactionRunner");
while (accts.hasNext()) {
acct = accts.next();
out.write("starting acct: " + acct);
id = acct.getOwner().getId();
cb = new Runnable() {
run() {
out.println("processing " + id);
u = user(id);
empty = new com.dumbhippo.server.PersonViewExtra[]{};
pv = identitySpider.getPersonView(systemView, u, empty);
out.println("got view: " + pv);
pv.getLiveUser();
out.println("got live user: ");
}
};
runner.runTaskInNewTransaction(cb);
}
Fixing up scraped MySpace IDs
This code was run to re-find all the MySpace IDs we cache mapped from the external account handle stored; we had NULL for a number of them when the scraper broke. To avoid this problem in the future we now prevent setting a myspace account if we can't scrape the ID.
myspaces = em.createQuery("from ExternalAccount ea where ea.sentiment=2 and ea.accountType=0 and ea.extra is NULL").getResultList();
for (i = 0; i < myspaces.size(); i++) {
ea = myspaces.get(i);
try {
friendId = com.dumbhippo.services.MySpaceScraper.getFriendId(ea.getHandle());
ea.setExtra(friendId);
} catch (com.dumbhippo.services.TransientServiceException e) {
out.println("Can't find myspace friendid for " + ea.getHandle());
}
}
This next bit of code re-invokes the setMySpaceName HTTP method for each user who we didn't have a friend ID cached for - it illustrates the use of the TransactionRunner to create a nested transaction.
myspaces = em.createQuery("from ExternalAccount ea where ea.sentiment=2 and ea.accountType=0 and ea.handle IS NOT NULL and ea.extra IS NULL and ea.feed is NULL order by id").setMaxResults(50).getResultList();
for (i = 0; i < myspaces.size(); i++) {
Thread.sleep(1000);
ea = myspaces.get(i);
ejb("TransactionRunner").runTaskInNewTransaction(new java.util.concurrent.Callable() {
public Object call() throws Exception {
try {
ea = em.find(com.dumbhippo.persistence.ExternalAccount.class, ea.getId());
ejb("HttpMethods").doSetMySpaceName(new com.dumbhippo.XmlBuilder(),
new com.dumbhippo.server.views.UserViewpoint(ea.getAccount().getOwner()),
ea.getHandle());
} catch (Throwable e) {
out.println("Failed to set name for " + ea.getHandle() + ": " + e);
}
return null;
}
});
}
Disabling a post
You need to know the post id, in this case it is "68a9fe4fa28070".
post = ejb("IdentitySpider").lookupGuidString(com.dumbhippo.persistence.Post.class, "68a9fe4fa28070");
ejb("PostingBoard").setPostDisabled(post, true);
Changing group membership policy
The list of groups needs to include ALL public groups that should remain by invitation. Alternatively, ids of specific public groups can be specified. This exact command should most likely never be used again because it only has ids of the groups that had to remain "By Invitation" as of 04/27/07, but new groups like that will be created! Run this without the last line in the loop first to check what groups will be converted. The action needs to be done by a Mugshot character because it has a special privilege to change membership policy for groups it is not a member of.
mugshot = user("mugshot@mugshot.org");
publicGroups = em.createQuery("from Group g where g.access=1 " +
"and g.id not in ('hLRZBlW7rLrkGT', 'VLfk6sCVRDvn4p', " +
"'plnRwNYHxkhj0y', 'TDMnsbdvqk3PhL', " +
"'wdNcvAMCS1FhXK', 'RymgjpqQA7rWSG', " +
"'DrZ42qVjmPWmnh', 'FrTLvK3zMTr95l', " +
"'RWyGRm0cNdZ48D', 'QZRFyRQz06Mzpx', " +
"'cMWStKmFPbAVQS', 'FBZV4K2nrwsT9L', " +
"'85TkT2RLw0rdwy', 'CvWWx7GN474Gs1', " +
"'KyMn6M0l11Fnsr', '1jhDsgtbPyP9Tw')").getResultList();
for (i = 0; i < publicGroups.size(); i++) {
publicGroupId = publicGroups.get(i).getId();
out.println("will change group membership policy for " + publicGroups.get(i).getName());
ejb("GroupSystem").reviseGroupMembershipPolicy(mugshot, publicGroupId, true);
}
If mysql won't start up
MySQL gets grumpy if you change the size of the Innodb data file or log file. There are instructions to deal with this in the mysql manual.
We believe it's safe to remove the log files and let new ones be created if the database was shut down cleanly. (These files are like a journal from a journaling file system?)
To change the size of the data files, you have to dump the db, delete the old data file, and reload.
But you should probably read the above link for full docs straight from the manual.
Flushing the Hibernate Cache
// flush a cached entity
em.getHibernateSession().getFactory().evictEntity("com.dumbhippo.persistence.Account")
// flush a cached collection
em.getHibernateSession().getFactory().evictCollection("com.dumbhippo.persistence.Account.clients")

