package org.basex.http.rest; import static org.basex.util.Token.*; import java.io.*; import java.util.*; import java.util.Map.Entry; import org.basex.core.*; import org.basex.core.cmd.*; import org.basex.core.locks.*; import org.basex.core.users.*; import org.basex.http.*; import org.basex.io.out.*; import org.basex.query.value.item.*; import org.basex.query.value.node.*; import org.basex.util.*; import org.basex.util.list.*; /** * Abstract class for performing REST operations. * * @author BaseX Team 2005-17, BSD License * @author Christian Gruen */ abstract class RESTCmd extends Command { /** REST session. */ final RESTSession session; /** Return code (may be {@code null}). */ HTTPCode code; /** * Constructor. * @param session REST session */ RESTCmd(final RESTSession session) { super(max(session)); this.session = session; jc().type(RESTText.REST); } @Override public void addLocks() { final Locks locks = jc().locks; final LockList reads = locks.reads, writes = locks.writes; for(final Command cmd : session) { cmd.addLocks(); // if command updates the context, it may affect any database that has been opened before. // hence, all read locks will be added to list of write locks final Locks cmdLocks = cmd.jc().locks; if(cmdLocks.writes.contains(Locking.CONTEXT)) writes.add(reads); // merge lock lists reads.add(cmdLocks.reads); writes.add(cmdLocks.writes); } } @Override public boolean updating(final Context ctx) { for(final Command cmd : session) updating |= cmd.updating(ctx); return updating; } @Override protected final boolean run() { try { run0(); return true; } catch(final IOException ex) { return error(ex.getMessage()); } finally { Close.close(context); } } /** * Runs the command. * @throws IOException I/O exception */ protected abstract void run0() throws IOException; /** * Runs the specified command. * @param cmd command * @return string result * @throws HTTPException HTTP exception */ final String run(final Command cmd) throws HTTPException { final ArrayOutput ao = new ArrayOutput(); run(cmd, ao); return ao.toString(); } /** * Runs the specified command. * @param cmd command * @param os output stream * @throws HTTPException HTTP exception */ final void run(final Command cmd, final OutputStream os) throws HTTPException { try { final boolean ok = pushJob(cmd).run(context, os); // only return info of last command final String info = cmd.info(); error(info); if(!ok) { if(cmd instanceof Open) code = HTTPCode.NOT_FOUND_X; throw HTTPCode.BAD_REQUEST_X.get(info); } } finally { popJob(); } } /** * Lists the table contents. * @param table table reference * @param root root node * @param header table header * @param skip number of columns to skip */ static void list(final Table table, final FElem root, final QNm header, final int skip) { for(final TokenList list : table.contents) { final FElem el = new FElem(header); // don't show last attribute (input path) final int ll = list.size() - skip; for(int l = 1; l < ll; l++) { el.add(new QNm(lc(table.header.get(l))), list.get(l)); } el.add(list.get(0)); root.add(el); } } /** * Returns the strictest permission required for the specified commands. * @param session commands to be checked * @return permission */ private static Perm max(final RESTSession session) { Perm p = Perm.NONE; for(final Command cmd : session) p = p.max(cmd.perm); return p; } /** * Parses and sets database options. * Throws an exception if an option is unknown. * @param session REST session * @throws IOException I/O exception */ static void parseOptions(final RESTSession session) throws IOException { for(final Entry<String, String[]> param : session.conn.params.map().entrySet()) parseOption(session, param, true); } /** * Parses and sets a single database option. * @param session REST session * @param param current parameter * @param force force execution * @return success flag, indicates if value was found * @throws BaseXException database exception */ static boolean parseOption(final RESTSession session, final Entry<String, String[]> param, final boolean force) throws BaseXException { final String key = param.getKey().toUpperCase(Locale.ENGLISH); final MainOptions options = session.conn.context.options; final boolean found = options.option(key) != null; if(found || force) options.assign(key, param.getValue()[0]); return found; } }