| /* |
| * Copyright (c) 2010-2013, 2015 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.branch.CDOBranch; |
| 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("="); |
| 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); |
| } |
| |
| 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(); |
| } |
| |
| /** |
| * 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 = CDOCommonUtil.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", CDOCommonUtil.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"); |
| 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 = CDOCommonUtil.formatTimeStamp(commitInfo.getTimeStamp()).replaceAll(" ", " "); |
| 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"); |
| |
| 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"); |
| } |
| } |
| } |