blob: deb621ec44a03e95b82ceb45f21f6e8c3ca6c4f2 [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jdo.impl.fostore;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeSet;
import org.apache.jdo.util.I18NHelper;
/**
* Process dump requests. A dump request provides information about the
* store. Currently, two options are supported:
* <ul>
* <li>dbInfo: Provide information about all classes stored in the store.</li>
* <li>ClassMetadata: Provide the stored metadata information for the class
* <code>className</code>.</li>
* </ul>
*
* @see org.apache.jdo.impl.fostore.DumpOption
* @author Markus Fuchs
*/
// This is server-side code. It does not need to live in the client.
class DumpHandler extends RequestHandler {
/** Maps <code>DumpOptions</code> to
* <code>DumpOptionSubHandlers</code>. The option table must match
* the <code>optionTable</code> in <code>Dumper</code>.
* <p>
* Because the <code>DumpOptionSubHandler</code> are bound to
* the state of the enclosing <code>DumpHander</code> instance, the
* <code>optionTable</code> has to be non-static. */
private static HashMap optionTable = new HashMap();
/** List of results. A SubRequest might report more than one result. */
private final ArrayList results = new ArrayList();
/** Return status. */
private Status status;
/** I18N support. */
private static final I18NHelper msg = I18NHelper.getInstance(I18N.NAME);
/** Initializes the <code>optionTable</code>.
* The option table must match the <code>optionTable</code> in
* <code>Dumper</code>.
* @see Dumper
*/
// Note that there is no entry for DumpOption.CLASS_INSTANCES: that option
// is handled entirely by the Dumper on the client side, with help of
// FOStoreStoreManager.getExtent.
private void initOptionTable() {
optionTable.put(DumpOption.DBINFO,
new DBInfoHandler());
optionTable.put(DumpOption.CLASS_METADATA,
new ClassMetadataHandler());
optionTable.put(DumpOption.CLASS_SUBCLASSES,
new ClassSubclassesHandler());
}
private DumpHandler(Reply reply, int length,
FOStoreServerConnection con) {
super(reply, length, con);
initOptionTable();
}
public static final HandlerFactory factory =
new HandlerFactory() {
public RequestHandler getHandler(Reply reply, int length,
FOStoreServerConnection con) {
return new DumpHandler(reply, length, con);
}};
RequestFinisher handleRequest()
throws IOException, FOStoreDatabaseException {
FOStoreInput in = con.getInputFromClient();
FOStoreDatabase db = con.getDatabase();
// read arguments
DumpOption option = DumpOption.read(in);
String className = in.readUTF();
DumpOptionSubHandler h =
(DumpOptionSubHandler) optionTable.get(option);
if (null == h) {
reply.setStatus(Status.FATAL,msg.msg("ERR_NoSubHandler", option)); // NOI18N
} else {
h.run(db, className);
int size = results.size();
reply.writeInt(size);
if (size > 0) {
for (int i = 0; i < size; i++) {
Object o = results.get(i);
if (null != o) {
reply.writeUTF(o.toString());
}
}
}
reply.setStatus(status);
}
return null;
}
/**
* Abstract class for dumping database information. The server creates
* instances of subclasses and then invoke <code>run</code>. Subclasses
* implement <code>run</code> method to do the real work.
*/
abstract class DumpOptionSubHandler {
/** Must be implemented to dump particular kind of info.
*/
protected abstract void run(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException;
}
/**
* OptionHandler that dumps DBInfo.
*/
class DBInfoHandler extends DumpOptionSubHandler {
protected void run(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException {
results.add(db.getDBInfo());
status = Status.OK;
}
}
/**
* Abstract OptionHandler that assists in dumping information about
* classes.
*/
abstract class ClassHandler extends DumpOptionSubHandler {
protected void run(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException {
if (null == className) {
reply.setStatus(
Status.ERROR,
msg.msg("MSG_MissingParameter", className)); //NOI18N
} else {
_run(db, className);
}
}
DBClass[] getDBClasses(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException {
DBClass rc[] = null;
// look for class info in store
DBInfo dbInfo = db.getDBInfo();
ArrayList found = new ArrayList();
for (Iterator i = dbInfo.getDBClasses(); i.hasNext();) {
DBClass dbClass = (DBClass)i.next();
if (className.equals(dbClass.getName())) {
found.add(dbClass);
}
}
int size = found.size();
if (size > 0) {
rc = new DBClass[size];
for (int i = 0; i < size; i++) {
rc[i] = (DBClass)found.get(i);
}
}
return rc;
}
/** Subclasses must implement, to dump their particular kind of info.
*/
abstract void _run(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException;
}
/**
* OptionHandler that dumps metadata about a class.
*/
class ClassMetadataHandler extends ClassHandler {
protected void _run(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException {
DBClass dbClasses[] = getDBClasses(db, className);
for (int i = 0; i < dbClasses.length; i++) {
results.add(dbClasses[i]);
}
status = Status.OK;
}
}
/**
* OptionHandler that dumps information about the subclasses of a class.
*/
class ClassSubclassesHandler extends ClassHandler {
protected void _run(FOStoreDatabase db, String className)
throws IOException, FOStoreDatabaseException {
// Sort results in String order.
TreeSet sorted = new TreeSet();
DBClass dbClasses[] = getDBClasses(db, className);
if (null == dbClasses || 0 == dbClasses.length){
results.add(
msg.msg("MSG_DbClassNotFound", className)); // NOI18N
} else {
// For each DBClass known by className, get all of its
// subclasses.
for (int i = 0; i < dbClasses.length; i++) {
String result = null;
CLID clid = dbClasses[i].getCLID();
OID ssOID = DBInfo.getSubclassSetOID(clid);
SubclassSet sl = (SubclassSet)db.getIfExists(ssOID);
if (null == sl) {
result = msg.msg("MSG_NoSubclasses", className); // NOI18N
} else {
result = msg.msg("MSG_Subclasses", className); // NOI18N
// Get results into the TreeSet
for (Iterator j = sl.iterator(); j.hasNext();) {
CLID subCLID = (CLID)j.next();
OID subDBClassOID = DBInfo.getDBClassOID(subCLID);
DBClass subDBClass =
(DBClass)db.get(subDBClassOID);
sorted.add("\n\t" + subDBClass.getName()); // NOI18N
}
// Put sorted subclasses from TreeSet to string
for (Iterator k = sorted.iterator(); k.hasNext();) {
result += (String)k.next();
}
}
results.add(result);
}
}
status = Status.OK;
}
}
}