/* * Copyright (c) 2010-2013, 2015, 2016 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eike Stepper - initial API and implementation */ package org.eclipse.emf.cdo.server; import org.eclipse.emf.cdo.common.CDOCommonRepository; import org.eclipse.emf.cdo.common.CDOCommonRepository.CommitInfoStorage; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; import org.eclipse.emf.cdo.common.branch.CDOBranchVersion; import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; import org.eclipse.emf.cdo.common.commit.CDOCommitInfoHandler; import org.eclipse.emf.cdo.common.id.CDOID; import org.eclipse.emf.cdo.common.id.CDOIDUtil; import org.eclipse.emf.cdo.common.lob.CDOLobHandler; import org.eclipse.emf.cdo.common.lob.CDOLobInfo; import org.eclipse.emf.cdo.common.lock.IDurableLockingManager.LockGrade; import org.eclipse.emf.cdo.common.revision.CDOAllRevisionsProvider; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.common.revision.CDORevision; import org.eclipse.emf.cdo.common.revision.CDORevisionKey; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil; import org.eclipse.emf.cdo.common.revision.CDORevisionUtil.AllRevisionsDumper; import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; import org.eclipse.emf.cdo.common.util.CDOCommonUtil; import org.eclipse.emf.cdo.internal.server.bundle.OM; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageInfo; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageRegistry; import org.eclipse.emf.cdo.spi.common.model.InternalCDOPackageUnit; import org.eclipse.emf.cdo.spi.common.revision.DetachedCDORevision; import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; import org.eclipse.emf.cdo.spi.common.revision.PointerCDORevision; import org.eclipse.emf.cdo.spi.common.revision.SyntheticCDORevision; import org.eclipse.emf.cdo.spi.server.InternalLockManager; import org.eclipse.emf.cdo.spi.server.InternalRepository; import org.eclipse.emf.cdo.spi.server.InternalSession; import org.eclipse.emf.cdo.spi.server.InternalView; import org.eclipse.net4j.util.HexUtil; import org.eclipse.net4j.util.StringUtil; import org.eclipse.net4j.util.WrappedException; import org.eclipse.net4j.util.concurrent.Worker; import org.eclipse.net4j.util.container.ContainerEventAdapter; import org.eclipse.net4j.util.container.IContainer; import org.eclipse.net4j.util.container.IManagedContainer; import org.eclipse.net4j.util.container.IPluginContainer; import org.eclipse.net4j.util.event.IListener; import org.eclipse.net4j.util.factory.ProductCreationException; import org.eclipse.net4j.util.io.IOUtil; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnum; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.CharArrayWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.Writer; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; /** * A simple HTTP server that web browsers can connect to in order to render internal server data for debugging purposes. * <p> * Actual content is contributed through pluggable {@link CDOServerBrowser.Page pages}. * <p> * <b>Note:</b> Don't use this server in production, it's unsecure and does not perform or scale! * * @author Eike Stepper * @since 4.0 */ public class CDOServerBrowser extends Worker { private static final String REQUEST_PREFIX = "GET "; private static final String REQUEST_SUFFIX = " HTTP/1.1"; private ThreadLocal<Map<String, String>> params = new InheritableThreadLocal<Map<String, String>>() { @Override protected Map<String, String> initialValue() { return new HashMap<String, String>(); } }; private int port = 7777; private ServerSocket serverSocket; private Map<String, InternalRepository> repositories; private List<Page> pages = new ArrayList<Page>(); public CDOServerBrowser(Map<String, InternalRepository> repositories) { this.repositories = repositories; setDaemon(true); } public Map<String, InternalRepository> getRepositories() { return repositories; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } @Override protected void work(WorkContext context) throws Exception { Socket socket = null; try { socket = serverSocket.accept(); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); OutputStream out = new BufferedOutputStream(socket.getOutputStream()); PrintStream pout = new PrintStream(out); printHeader(pout); String line; while ((line = in.readLine()) != null) { if (line.startsWith(REQUEST_PREFIX) && line.endsWith(REQUEST_SUFFIX)) { String request = line.substring(REQUEST_PREFIX.length(), line.length() - REQUEST_SUFFIX.length()).trim(); String resource = request; String params = ""; int pos = request.indexOf('?'); if (pos != -1) { resource = request.substring(0, pos); params = request.substring(pos + 1); } initParams(params); if ("/".equals(resource)) { showMenu(pout); } else { String pageName = resource.substring(1); for (Page page : pages) { if (page.getName().equals(pageName)) { showPage(pout, page); } } } } out.flush(); return; } } catch (Exception ex) { if (isActive()) { ex.printStackTrace(); } } finally { params.remove(); if (socket != null) { socket.close(); } } } protected void initParams(String params) { Map<String, String> map = this.params.get(); for (String param : params.split("&")) { if (param.length() != 0) { String[] keyValue = param.split("="); if (keyValue.length == 1) { map.put(keyValue[0], "true"); } else { map.put(keyValue[0], keyValue[1]); } } } } protected void clearParams() { Map<String, String> map = params.get(); map.clear(); } public void removeParam(String key) { Map<String, String> map = params.get(); map.remove(key); } public String getParam(String key) { Map<String, String> map = params.get(); return map.get(key); } /** * @since 4.5 */ public boolean isParam(String key) { Map<String, String> map = params.get(); return "true".equalsIgnoreCase(map.get(key)); } public String href(String label, String resource, String... params) { Map<String, String> map = new HashMap<String, String>(this.params.get()); for (int i = 0; i < params.length;) { map.put(params[i++], params[i++]); } List<String> list = new ArrayList<String>(map.keySet()); Collections.sort(list); StringBuilder builder = new StringBuilder(); for (String key : list) { String value = map.get(key); if (value != null) { if (builder.length() != 0) { builder.append("&"); } builder.append(key); builder.append("="); builder.append(value); } } return "<a href=\"/" + escape(resource) + "?" + escape(builder.toString()) + "\">" + escape(label) + "</a>"; } public String escape(String raw) { if (raw == null) { return "null"; } return raw.replace("<", "<"); } protected void printHeader(PrintStream pout) { pout.print("HTTP/1.1 200 OK\r\n"); pout.print("Content-Type: text/html\r\n"); pout.print("Date: " + new Date() + "\r\n"); pout.print("Server: DBBrowser 3.0\r\n"); pout.print("\r\n"); } protected void showMenu(PrintStream pout) { clearParams(); pout.print("<h1>CDO Server Browser 4.0</h1><hr>\r\n"); for (Page page : pages) { pout.println("<h3>" + href(page.getLabel(), page.getName()) + "</h3>"); } } protected void showPage(PrintStream pout, Page page) { String repo = getParam("repo"); List<String> repoNames = new ArrayList<String>(getRepositoryNames()); Collections.sort(repoNames); pout.print("<h3><a href=\"/\">" + page.getLabel() + "</a>:  "); for (String repoName : repoNames) { InternalRepository repository = getRepository(repoName); if (!page.canDisplay(repository)) { continue; } if (repo == null) { repo = repoName; } if (repoName.equals(repo)) { pout.print("<b>" + escape(repoName) + "</b>  "); } else { pout.print(href(repoName, page.getName(), "repo", repoName) + "  "); } } pout.print("</h3>"); InternalRepository repository = getRepository(repo); if (repository != null) { pout.print("<p>\r\n"); InternalSession session = repository.getSessionManager().openSession(null); StoreThreadLocal.setSession(session); try { page.display(this, repository, pout); } finally { StoreThreadLocal.release(); session.close(); } } } protected Set<String> getRepositoryNames() { return repositories.keySet(); } protected InternalRepository getRepository(String name) { return repositories.get(name); } @Override protected String getThreadName() { return "CDOServerBrowser"; } protected void initPages(List<Page> pages) { pages.add(new PackagesPage()); pages.add(new LocksPage()); pages.add(new RevisionsPage.FromCache()); pages.add(new RevisionsPage.FromStore()); pages.add(new LobsPage()); pages.add(new HistoryPage()); IManagedContainer container = getPagesContainer(); for (String factoryType : container.getFactoryTypes(Page.PRODUCT_GROUP)) { try { Page page = (Page)container.getElement(Page.PRODUCT_GROUP, factoryType, null); pages.add(page); } catch (Exception ex) { OM.LOG.error(ex); } } } /** * @since 4.1 */ protected IManagedContainer getPagesContainer() { return IPluginContainer.INSTANCE; } @Override protected void doActivate() throws Exception { initPages(pages); try { serverSocket = new ServerSocket(port); } catch (Exception ex) { throw new IllegalStateException("Could not open socket on port " + port, ex); } super.doActivate(); } @Override protected void doDeactivate() throws Exception { serverSocket.close(); super.doDeactivate(); } /** * @since 4.5 */ public static String formatTimeStamp(long timeStamp) { String str = CDOCommonUtil.formatTimeStamp(timeStamp); if (!CDOCommonUtil.UNSPECIFIED_DATE_STRING.equals(str)) { str += " - " + timeStamp; str = str.replaceAll(" ", " "); } return str; } /** * A {@link CDOServerBrowser server browser} for the repositories in a {@link IManagedContainer managed container}. * * @author Eike Stepper */ public static class ContainerBased extends CDOServerBrowser { private IContainer<?> container; private IListener containerListener = new ContainerEventAdapter<Object>() { @Override protected void onAdded(IContainer<Object> container, Object element) { addElement(element); } @Override protected void onRemoved(IContainer<Object> container, Object element) { removeElement(element); } }; public ContainerBased(IContainer<?> container) { super(new HashMap<String, InternalRepository>()); this.container = container; } public ContainerBased() { this(IPluginContainer.INSTANCE); } public IContainer<?> getContainer() { return container; } @Override protected IManagedContainer getPagesContainer() { if (container instanceof IManagedContainer) { return (IManagedContainer)container; } return IPluginContainer.INSTANCE; } @Override protected void doActivate() throws Exception { super.doActivate(); for (Object element : container.getElements()) { addElement(element); } container.addListener(containerListener); } @Override protected void doDeactivate() throws Exception { container.removeListener(containerListener); super.doDeactivate(); } private void addElement(Object element) { if (element instanceof InternalRepository) { InternalRepository repository = (InternalRepository)element; getRepositories().put(repository.getName(), repository); } } private void removeElement(Object element) { if (element instanceof InternalRepository) { InternalRepository repository = (InternalRepository)element; getRepositories().remove(repository.getName()); } } /** * Creates {@link CDOServerBrowser server browsers} for the repositories in a {@link IManagedContainer managed * container}. * * @author Eike Stepper */ public static class Factory extends org.eclipse.net4j.util.factory.Factory { public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.browsers"; public static final String TYPE = "default"; private IContainer<?> container; public Factory() { this(IPluginContainer.INSTANCE); } public Factory(IContainer<?> container) { super(PRODUCT_GROUP, TYPE); this.container = container; } public CDOServerBrowser.ContainerBased create(String description) throws ProductCreationException { CDOServerBrowser.ContainerBased browser = new CDOServerBrowser.ContainerBased(container); try { int port = 0; if (!StringUtil.isEmpty(description)) { int digits = 0; for (int i = 0; i < description.length(); i++) { if (Character.isDigit(description.charAt(i))) { ++digits; } else { break; } } if (digits != 0) { port = Integer.parseInt(description.substring(0, digits)); } } if (port == 0) { port = IOUtil.getFreePort(); } browser.setPort(port); } catch (Exception ex) { OM.LOG.warn(ex); } return browser; } } } /** * Represents pluggable content for a {@link CDOServerBrowser server browser}. * * @author Eike Stepper */ public static interface Page { public static final String PRODUCT_GROUP = "org.eclipse.emf.cdo.server.browserPages"; public String getName(); public String getLabel(); public boolean canDisplay(InternalRepository repository); public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out); } /** * An abstract base implementation of a {@link Page server browser page}. * * @author Eike Stepper */ public static abstract class AbstractPage implements Page { private String name; private String label; public AbstractPage(String name, String label) { this.name = name; this.label = label; } public String getName() { return name; } public String getLabel() { return label; } } /** * A {@link Page server browser page} that renders the package registry contents of a repository. * * @author Eike Stepper */ public static class PackagesPage extends AbstractPage { public static final String NAME = "packages"; public PackagesPage() { super(NAME, "Packages and Classes"); } public boolean canDisplay(InternalRepository repository) { return true; } public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out) { String param = browser.getParam("classifier"); InternalCDOPackageRegistry packageRegistry = repository.getPackageRegistry(false); for (InternalCDOPackageUnit unit : packageRegistry.getPackageUnits()) { param = showPackage(unit.getTopLevelPackageInfo(), packageRegistry, browser, param, out, "  "); } } protected String showPackage(InternalCDOPackageInfo info, InternalCDOPackageRegistry packageRegistry, CDOServerBrowser browser, String param, PrintStream out, String prefix) { EPackage ePackage = info.getEPackage(); out.println("<h3>" + prefix + ePackage.getName() + "  [" + ePackage.getNsURI() + "]</h3>"); for (EClassifier classifier : ePackage.getEClassifiers()) { String name = classifier.getName(); if (param == null) { param = name; } String label = name.equals(param) ? name : browser.href(name, getName(), "classifier", name); out.print(prefix + "  <b>" + label); if (classifier instanceof EEnum) { EEnum eenum = (EEnum)classifier; out.print("  " + eenum.getELiterals()); } else if (classifier instanceof EDataType) { EDataType eDataType = (EDataType)classifier; out.print("  " + eDataType.getInstanceClassName()); } out.println("</b><br>"); } for (EPackage sub : ePackage.getESubpackages()) { InternalCDOPackageInfo subInfo = packageRegistry.getPackageInfo(sub); param = showPackage(subInfo, packageRegistry, browser, param, out, prefix + "  "); } return param; } } /** * A {@link Page server browser page} that renders the locking manager contents of a repository. * * @author Eike Stepper * @since 4.2 */ public static class LocksPage extends AbstractPage { public static final String NAME = "locks"; public LocksPage() { super(NAME, "Locks"); } public boolean canDisplay(InternalRepository repository) { return true; } public void display(CDOServerBrowser browser, InternalRepository repository, PrintStream out) { InternalLockManager lockingManager = repository.getLockingManager(); for (InternalSession session : repository.getSessionManager().getSessions()) { boolean sessionRendered = false; for (InternalView view : session.getViews()) { Map<CDOID, LockGrade> locks = lockingManager.getLocks(view); if (locks != null && !locks.isEmpty()) { if (!sessionRendered) { int sessionID = session.getSessionID(); String userID = session.getUserID(); out.println("<h3>Session " + sessionID + "  [" + userID + "]</h3>"); out.println("<ul>"); sessionRendered = true; } out.println("<li>" + view + "</li>"); out.println("<ul>"); for (Entry<CDOID, LockGrade> entry : locks.entrySet()) { out.println("<li>" + entry.getKey() + " = " + entry.getValue() + "</li>"); } out.println("</ul>"); } if (sessionRendered) { out.println("</ul>"); } } } } } /** * A {@link Page server browser page} that renders {@link CDORevision revisions}. * * @author Eike Stepper */ public static abstract class RevisionsPage extends AbstractPage { public RevisionsPage(String name, String label) { super(name, label); } public void display(final CDOServerBrowser browser, InternalRepository repository, PrintStream out) { Map<CDOBranch, List<CDORevision>> allRevisions = getAllRevisions(repository); Map<CDOID, List<CDORevision>> ids = getAllIDs(allRevisions); out.print("<table border=\"0\">\r\n"); out.print("<tr>\r\n"); out.print("<td valign=\"top\">\r\n"); out.print("<table border=\"1\" cellpadding=\"2\"><tr><td>\r\n"); final String[] revision = { browser.getParam("revision") }; new AllRevisionsDumper.Stream.Html(allRevisions, out) { private StringBuilder versionsBuilder; private CDORevision lastRevision; @Override protected void dumpEnd(List<CDOBranch> branches) { dumpLastRevision(); super.dumpEnd(branches); } @Override protected void dumpBranch(CDOBranch branch) { dumpLastRevision(); super.dumpBranch(branch); } @Override protected void dumpRevision(CDORevision rev) { CDOID id = rev.getID(); if (lastRevision != null && !id.equals(lastRevision.getID())) { dumpLastRevision(); } if (versionsBuilder == null) { versionsBuilder = new StringBuilder(); } else { versionsBuilder.append(" "); } String key = CDORevisionUtil.formatRevisionKey(rev); if (revision[0] == null) { revision[0] = key; } String version = getVersionPrefix(rev) + rev.getVersion(); if (key.equals(revision[0])) { versionsBuilder.append("<b>" + version + "</b>"); } else { versionsBuilder.append(browser.href(version, getName(), "revision", key)); } lastRevision = rev; } protected void dumpLastRevision() { if (versionsBuilder != null) { PrintStream out = out(); out.println("<tr>"); out.println("<td valign=\"top\">    "); out.println(getCDOIDLabel(lastRevision)); out.println("    </td>"); out.println("<td>"); out.println(versionsBuilder.toString()); out.println("</td>"); out.println("</tr>"); lastRevision = null; versionsBuilder = null; } } }.dump(); out.print("</td></tr></table></td>\r\n"); out.print("<td>   </td>\r\n"); if (revision[0] != null) { out.print("<td valign=\"top\">\r\n"); showRevision(out, browser, allRevisions, ids, revision[0], repository); out.print("</td>\r\n"); } out.print("</tr>\r\n"); out.print("</table>\r\n"); } /** * @since 4.0 */ protected void showRevision(PrintStream pout, CDOServerBrowser browser, Map<CDOBranch, List<CDORevision>> allRevisions, Map<CDOID, List<CDORevision>> ids, String key, InternalRepository repository) { CDORevisionKey revisionKey = CDORevisionUtil.parseRevisionKey(key, repository.getBranchManager()); for (CDORevision revision : allRevisions.get(revisionKey.getBranch())) { if (revision.getVersion() == revisionKey.getVersion() && revision.getID().equals(revisionKey.getID())) { showRevision(pout, browser, ids, (InternalCDORevision)revision); return; } } } /** * @since 4.0 */ protected void showRevision(PrintStream pout, CDOServerBrowser browser, Map<CDOID, List<CDORevision>> ids, InternalCDORevision revision) { String className = revision.getEClass().toString(); className = className.substring(className.indexOf(' ')); className = StringUtil.replace(className, new String[] { "(", ")", "," }, new String[] { "<br>", "", "<br>" }); className = className.substring("<br>".length() + 1); String created = formatTimeStamp(revision.getTimeStamp()); String commitInfo = browser.href(created, HistoryPage.NAME, "time", String.valueOf(revision.getTimeStamp())); pout.print("<table border=\"1\" cellpadding=\"2\">\r\n"); showKeyValue(pout, true, "type", "<b>" + revision.getClass().getSimpleName() + "</b>"); showKeyValue(pout, true, "class", className); showKeyValue(pout, true, "id", getRevisionValue(revision.getID(), browser, ids, revision)); showKeyValue(pout, true, "branch", revision.getBranch().getName() + "[" + revision.getBranch().getID() + "]"); showKeyValue(pout, true, "version", revision.getVersion()); showKeyValue(pout, true, "created", commitInfo); showKeyValue(pout, true, "revised", formatTimeStamp(revision.getRevised())); if (revision instanceof SyntheticCDORevision) { if (revision instanceof PointerCDORevision) { PointerCDORevision pointer = (PointerCDORevision)revision; CDOBranchVersion target = pointer.getTarget(); CDOBranch branch = target.getBranch(); int version = target.getVersion(); String label = getVersionLabel("v", version, branch); CDORevisionKey targetKey = CDORevisionUtil.createRevisionKey(pointer.getID(), branch, version); String value = CDORevisionUtil.formatRevisionKey(targetKey); showKeyValue(pout, true, "target", browser.href(label, getName(), "revision", value)); } } else { showKeyValue(pout, true, "resource", getRevisionValue(revision.getResourceID(), browser, ids, revision)); showKeyValue(pout, true, "container", getRevisionValue(revision.getContainerID(), browser, ids, revision)); showKeyValue(pout, true, "feature", revision.getContainingFeatureID()); for (EStructuralFeature feature : revision.getClassInfo().getAllPersistentFeatures()) { Object value = revision.getValue(feature); showKeyValue(pout, false, feature.getName(), getRevisionValue(value, browser, ids, revision)); } } pout.print("</table>\r\n"); } /** * @since 4.0 */ protected Object getRevisionValue(Object value, CDOServerBrowser browser, Map<CDOID, List<CDORevision>> ids, InternalCDORevision context) { if (value instanceof CDOID) { List<CDORevision> revisions = ids.get(value); if (revisions != null) { StringBuilder builder = new StringBuilder(); builder.append(getCDOIDLabel(revisions.get(0))); if (browser != null) { builder.append("  "); for (CDORevision revision : revisions) { String versionPrefix = getVersionPrefix(revision); int version = revision.getVersion(); CDOBranch branch = revision.getBranch(); String label = getVersionLabel(versionPrefix, version, branch); builder.append(" "); if (revision == context) { builder.append(label); } else { builder.append(browser.href(label, getName(), "revision", CDORevisionUtil.formatRevisionKey(revision))); } } } return builder.toString(); } } if (value instanceof Collection) { StringBuilder builder = new StringBuilder(); for (Object element : (Collection<?>)value) { builder.append(builder.length() == 0 ? "" : "<br>"); builder.append(getRevisionValue(element, browser, ids, context)); } return builder.toString(); } return value; } private String getVersionLabel(String versionPrefix, int version, CDOBranch branch) { String label = versionPrefix + version; String branchName = branch.getName(); if (!CDOBranch.MAIN_BRANCH_NAME.equals(branchName)) { label += "[" + branchName + "]"; } return label; } private String getVersionPrefix(CDORevision revision) { if (revision instanceof PointerCDORevision) { return "p"; } if (revision instanceof DetachedCDORevision) { return "d"; } return "v"; } /** * @since 4.0 */ protected void showKeyValue(PrintStream pout, boolean bg, String key, Object value) { String color = bg ? "EEEEEE" : "FFFFFF"; pout.print("<tr bgcolor=\"" + color + "\">\r\n"); pout.print("<td valign=\"top\"><b>" + key + "</b></td>\r\n"); pout.print("<td valign=\"top\">"); pout.print(value); pout.print("</td>\r\n"); pout.print("</tr>\r\n"); } protected abstract Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository); private Map<CDOID, List<CDORevision>> getAllIDs(Map<CDOBranch, List<CDORevision>> allRevisions) { Map<CDOID, List<CDORevision>> ids = CDOIDUtil.createMap(); for (List<CDORevision> list : allRevisions.values()) { for (CDORevision revision : list) { CDOID id = revision.getID(); List<CDORevision> revisions = ids.get(id); if (revisions == null) { revisions = new ArrayList<CDORevision>(); ids.put(id, revisions); } revisions.add(revision); } } return ids; } protected String getCDOIDLabel(CDORevision revision) { String label = revision.toString(); return label.substring(0, label.indexOf(':')); } /** * A {@link Page server browser page} that renders the {@link CDORevision revisions} in a revision cache. * * @author Eike Stepper */ public static class FromCache extends RevisionsPage { public static final String NAME = "crevisions"; public FromCache() { super(NAME, "Revisions From Cache"); } public boolean canDisplay(InternalRepository repository) { return true; } @Override protected Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository) { return repository.getRevisionManager().getCache().getAllRevisions(); } } /** * A {@link Page server browser page} that renders the {@link CDORevision revisions} in a {@link IStore store}. * * @author Eike Stepper */ public static class FromStore extends RevisionsPage { public static final String NAME = "srevisions"; public FromStore() { super(NAME, "Revisions From Store"); } public boolean canDisplay(InternalRepository repository) { return repository.getStore() instanceof CDOAllRevisionsProvider; } @Override protected Map<CDOBranch, List<CDORevision>> getAllRevisions(InternalRepository repository) { return ((CDOAllRevisionsProvider)repository.getStore()).getAllRevisions(); } } } /** * A {@link Page server browser page} that renders {@link CDOLobInfo large object infos}. * * @author Eike Stepper */ public static class LobsPage extends AbstractPage { public static final String NAME = "lobs"; public LobsPage() { super(NAME, "Large Objects"); } public boolean canDisplay(InternalRepository repository) { return true; } public void display(final CDOServerBrowser browser, InternalRepository repository, final PrintStream out) { out.print("<table border=\"0\">\r\n"); out.print("<tr>\r\n"); out.print("<td valign=\"top\">\r\n"); final String param = browser.getParam("id"); final Object[] details = { null, null, null }; try { repository.handleLobs(0, 0, new CDOLobHandler() { public OutputStream handleBlob(byte[] id, long size) { if (showLob(out, "Blob", id, size, browser, param)) { ByteArrayOutputStream result = new ByteArrayOutputStream(); details[0] = result; details[1] = param; details[2] = size; return result; } return null; } public Writer handleClob(byte[] id, long size) { if (showLob(out, "Clob", id, size, browser, param)) { CharArrayWriter result = new CharArrayWriter(); details[0] = result; details[1] = param; details[2] = size; return result; } return null; } }); } catch (IOException ex) { throw WrappedException.wrap(ex); } out.print("</td>\r\n"); if (details[0] != null) { out.print("<td>   </td>\r\n"); out.print("<td valign=\"top\">\r\n"); if (details[0] instanceof ByteArrayOutputStream) { ByteArrayOutputStream baos = (ByteArrayOutputStream)details[0]; String hex = HexUtil.bytesToHex(baos.toByteArray()); out.println("<h3>Blob " + details[1] + " (" + details[2] + ")</h3>"); out.println("<pre>\r\n"); for (int i = 0; i < hex.length(); i++) { out.print(hex.charAt(i)); if ((i + 1) % 32 == 0) { out.print("\r\n"); } else if ((i + 1) % 16 == 0) { out.print(" "); } else if ((i + 1) % 2 == 0) { out.print(" "); } } out.println("</pre>\r\n"); } else { CharArrayWriter caw = (CharArrayWriter)details[0]; out.println("<h3>Clob " + details[1] + " (" + details[2] + ")</h3>"); out.println("<pre>" + caw + "</pre>"); } out.print("</td>\r\n"); } out.print("</tr>\r\n"); out.print("</table>\r\n"); } protected boolean showLob(PrintStream out, String type, byte[] id, long size, CDOServerBrowser browser, String param) { String hex = HexUtil.bytesToHex(id); boolean selected = hex.equals(param); String label = selected ? hex : browser.href(hex, getName(), "id", hex); out.println(type + " " + label + " (" + size + ")"); return selected; } } /** * A {@link Page server browser page} that renders {@link CDOCommitInfo commit infos}. * * @author Eike Stepper */ public static class HistoryPage extends AbstractPage { public static final String NAME = "history"; public HistoryPage() { super(NAME, "Commit Infos"); } public boolean canDisplay(InternalRepository repository) { return true; } public void display(final CDOServerBrowser browser, InternalRepository repository, final PrintStream out) { out.print("<table border=\"0\">\r\n"); out.print("<tr>\r\n"); out.print("<td valign=\"top\">\r\n"); IStoreAccessor accessor = repository.getStore().getReader(null); StoreThreadLocal.setAccessor(accessor); final String param = browser.getParam("time"); out.print("<table border=\"1\" cellpadding=\"2\">\r\n"); out.print("<tr>\r\n"); out.print("<td valign=\"top\">Time</td>\r\n"); out.print("<td valign=\"top\">Branch</td>\r\n"); out.print("<td valign=\"top\">User</td>\r\n"); out.print("<td valign=\"top\">Comment</td>\r\n"); if (repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE) { out.print("<td valign=\"top\">Merge</td>\r\n"); } out.print("</tr>\r\n"); final CDOCommitInfo[] details = { null }; try { final boolean auditing = repository.isSupportingAudits(); repository.getCommitInfoManager().getCommitInfos(null, 0L, 0L, new CDOCommitInfoHandler() { public void handleCommitInfo(CDOCommitInfo commitInfo) { if (showCommitInfo(out, commitInfo, browser, param, auditing)) { details[0] = commitInfo; } } }); out.print("</table>\r\n"); out.print("</td>\r\n"); out.print("<td>   </td>\r\n"); out.print("<td valign=\"top\">\r\n"); if (auditing) { CDOCommitInfo commitInfo = details[0]; if (commitInfo != null) { out.print("<h3>Commit Info " + commitInfo.getTimeStamp() + "</h3>\r\n"); showCommitData(out, commitInfo, browser); } } else { out.print("<h3>No audit data available in this repository.</h3>\r\n"); } out.print("</td>\r\n"); out.print("</tr>\r\n"); out.print("</table>\r\n"); } finally { StoreThreadLocal.release(); } } protected boolean showCommitInfo(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser, String param, boolean auditing) { String timeStamp = String.valueOf(commitInfo.getTimeStamp()); boolean selected = timeStamp.equals(param); String formatted = formatTimeStamp(commitInfo.getTimeStamp()); String label = formatted; if (!selected && auditing) { label = browser.href(formatted, getName(), "time", timeStamp); } out.print("<tr>\r\n"); out.print("<td valign=\"top\">\r\n"); out.print(label); out.print("</td>\r\n"); CDOBranch branch = commitInfo.getBranch(); out.print("<td valign=\"top\">\r\n"); out.print(branch.getName() + "[" + branch.getID() + "]"); out.print("</td>\r\n"); String userID = commitInfo.getUserID(); out.print("<td valign=\"top\">\r\n"); out.print(StringUtil.isEmpty(userID) ? " " : browser.escape(userID)); out.print("</td>\r\n"); String comment = commitInfo.getComment(); out.print("<td valign=\"top\">\r\n"); out.print(StringUtil.isEmpty(comment) ? " " : browser.escape(comment)); out.print("</td>\r\n"); CDOCommonRepository repository = commitInfo.getCommitInfoManager().getRepository(); if (repository.getCommitInfoStorage() == CommitInfoStorage.WITH_MERGE_SOURCE) { out.print("<td valign=\"top\">\r\n"); CDOBranchPoint mergeSource = commitInfo.getMergeSource(); if (mergeSource == null) { out.print(" "); } else { String mergeSourceLabel = browser.escape(mergeSource.getBranch().getPathName()) + " - " + formatTimeStamp(mergeSource.getTimeStamp()); out.print(browser.href(mergeSourceLabel, getName(), "time", String.valueOf(mergeSource.getTimeStamp()))); } out.print("</td>\r\n"); } out.print("</tr>\r\n"); return selected; } protected void showCommitData(PrintStream out, CDOCommitInfo commitInfo, CDOServerBrowser browser) { out.print("<h4>New Objects:</h4>\r\n"); out.print("<ul>\r\n"); for (CDOIDAndVersion key : commitInfo.getNewObjects()) { CDORevision newObject = (CDORevision)key; out.print( "<li>" + browser.href(newObject.toString(), RevisionsPage.FromStore.NAME, "revision", CDORevisionUtil.formatRevisionKey(newObject)) + "<br>\r\n"); } out.print("</ul>\r\n"); out.print("<h4>Changed Objects:</h4>\r\n"); out.print("<ul>\r\n"); for (CDORevisionKey key : commitInfo.getChangedObjects()) { CDORevisionDelta changedObject = (CDORevisionDelta)key; out.print("<li>" + changedObject.toString() + "<br>\r\n"); } out.print("</ul>\r\n"); out.print("<h4>Detached Objects:</h4>\r\n"); out.print("<ul>\r\n"); for (CDOIDAndVersion key : commitInfo.getDetachedObjects()) { out.print("<li>" + key.toString() + "<br>\r\n"); } out.print("</ul>\r\n"); } } }