Fix file download for rap. Add various links in the bundle detail editor.
authorBruno Sinou <bsinou@argeo.org>
Wed, 7 May 2014 21:18:36 +0000 (21:18 +0000)
committerBruno Sinou <bsinou@argeo.org>
Wed, 7 May 2014 21:18:36 +0000 (21:18 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@6967 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

plugins/org.argeo.slc.client.rap/META-INF/spring/commands.xml
plugins/org.argeo.slc.client.rap/src/main/java/org/argeo/slc/client/rap/OpenJcrFileService.java
plugins/org.argeo.slc.client.rap/src/main/java/org/argeo/slc/client/ui/specific/OpenJcrFile.java
plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/editors/BundleDetailPage.java

index 78adfc6cd2f8c6d51823b4cba7d6427cf4bdd8fd..f19c5510380927c08fc4ce310b649b4f8015557e 100644 (file)
@@ -5,15 +5,16 @@
         http://www.springframework.org/schema/beans/spring-beans.xsd">
 
        <!-- RAP Specific open JCR file service -->
-       <bean id="openJcrFileService" class="org.argeo.slc.client.rap.OpenJcrFileService"
-               scope="prototype" init-method="init" destroy-method="destroy">
-               <property name="repoService" ref="repoService" />
-       </bean>
+       <!-- <bean id="openJcrFileService" class="org.argeo.slc.client.rap.OpenJcrFileService" -->
+       <!-- scope="prototype" init-method="init" destroy-method="destroy"> -->
+       <!-- <property name="repoService" ref="repoService" /> -->
+       <!-- <property name="repositoryFactory" ref="repositoryFactory" /> -->
+       <!-- <property name="keyring" ref="keyring" /> -->
+       <!-- <property name="nodeRepository" ref="nodeRepository" /> -->
+       <!-- </bean> -->
 
        <bean id="org.argeo.slc.client.rap.openJcrFile" class="org.argeo.slc.client.ui.specific.OpenJcrFile"
                scope="prototype">
                <property name="repoService" ref="repoService" />
-               <!-- Workaround to only force instantiation -->
-               <property name="openJcrFileService" ref="openJcrFileService" />
        </bean>
 </beans>
\ No newline at end of file
index d64a10bb7ca7a8d6656b424bd9131b308b98086b..a4dfaf42c696bd89822fdc61ebaec6ab8344f099 100644 (file)
@@ -14,86 +14,58 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.commons.io.IOUtils;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.slc.SlcException;
-import org.argeo.slc.repo.RepoService;
 import org.eclipse.rwt.RWT;
 import org.eclipse.rwt.service.IServiceHandler;
-import org.eclipse.rwt.service.IServiceManager;
 
 /**
- * Basic Default service handler that retrieves and launch download of a file
- * stored in a JCR Repository
+ * Basic Default service handler that retrieves a file from a NT_FILE JCR node
+ * and launch the download.
  */
 public class OpenJcrFileService implements IServiceHandler {
 
-       public final static String ID = SlcRapPlugin.PLUGIN_ID + ".openJcrFileService";
-
-       // use local node repo and repository factory to retrieve and log to
-       // relevant repository
-       public final static String PARAM_REPO_NODE_PATH = "param.repoNodePath";
-       // use URI and repository factory to retrieve and ANONYMOUSLY log in
-       // relevant repository
-       public final static String PARAM_REPO_URI = "param.repoUri";
-       public final static String PARAM_WORKSPACE_NAME = "param.workspaceName";
-       public final static String PARAM_FILE_PATH = "param.filePath";
-
-       public final static String SCHEME_HOST_SEPARATOR = "://";
-
        /* DEPENDENCY INJECTION */
-       private RepoService repoService;
-
-       public OpenJcrFileService() {
-       }
+       final private Node fileNode;
 
-       public void init() {
-               IServiceManager manager = RWT.getServiceManager();
-               manager.registerServiceHandler(ID, this);
-       }
-
-       public void destroy() {
-               IServiceManager manager = RWT.getServiceManager();
-               manager.unregisterServiceHandler(ID);
+       public OpenJcrFileService(Node fileNode) {
+               this.fileNode = fileNode;
        }
 
        public void service() throws IOException, ServletException {
-               String repoNodePath = RWT.getRequest().getParameter(PARAM_REPO_NODE_PATH);
-               String repoUri = RWT.getRequest().getParameter(PARAM_REPO_URI);
-               String wkspName = RWT.getRequest().getParameter(PARAM_WORKSPACE_NAME);
-               String filePath = RWT.getRequest().getParameter(PARAM_FILE_PATH);
-
                // Get the file content
-               byte[] download = getFileAsByteArray(repoNodePath, repoUri, wkspName,
-                               filePath);
+               byte[] download = getFileAsByteArray();
 
                // Send the file in the response
                HttpServletResponse response = RWT.getResponse();
                response.setContentType("application/octet-stream");
                response.setContentLength(download.length);
-               String contentDisposition = "attachment; filename=\""
-                               + JcrUtils.lastPathElement(filePath) + "\"";
+               String contentDisposition = null;
+               try {
+                       contentDisposition = "attachment; filename=\""
+                                       + JcrUtils.lastPathElement(fileNode.getPath()) + "\"";
+               } catch (RepositoryException e) {
+                       throw new SlcException("Error while getting file Path " + fileNode,
+                                       e);
+               }
                response.setHeader("Content-Disposition", contentDisposition);
 
                try {
                        response.getOutputStream().write(download);
                } catch (IOException ioe) {
-                       throw new SlcException("Error while writing the file " + filePath
+                       throw new SlcException("Error while writing the file " + fileNode
                                        + " to the servlet response", ioe);
                }
        }
 
-       protected byte[] getFileAsByteArray(String repoNodePath, String repoUri,
-                       String wkspName, String filePath) {
+       protected byte[] getFileAsByteArray() {
+
                Session businessSession = null;
                try {
-                       businessSession = repoService.getRemoteSession(repoNodePath,
-                                       repoUri, wkspName);
-                       Node result = businessSession.getNode(filePath);
-
                        boolean isValid = true;
                        Node child = null;
-                       if (!result.isNodeType(NodeType.NT_FILE))
+                       if (!fileNode.isNodeType(NodeType.NT_FILE))
                                isValid = false;
                        else {
-                               child = result.getNode(Property.JCR_CONTENT);
+                               child = fileNode.getNode(Property.JCR_CONTENT);
                                if (!(child.isNodeType(NodeType.NT_RESOURCE) || child
                                                .hasProperty(Property.JCR_DATA)))
                                        isValid = false;
@@ -101,7 +73,7 @@ public class OpenJcrFileService implements IServiceHandler {
 
                        if (!isValid)
                                return null;
-                       
+
                        byte[] ba = null;
                        InputStream fis = null;
                        try {
@@ -109,10 +81,8 @@ public class OpenJcrFileService implements IServiceHandler {
                                                .getBinary().getStream();
                                ba = IOUtils.toByteArray(fis);
                        } catch (Exception e) {
-                               throw new SlcException(
-                                               "Stream error while opening file " + filePath
-                                                               + " from repo " + repoUri == null ? repoNodePath
-                                                               : repoUri, e);
+                               throw new SlcException("Stream error while opening file "
+                                               + fileNode, e);
                        } finally {
                                IOUtils.closeQuietly(fis);
                        }
@@ -120,16 +90,9 @@ public class OpenJcrFileService implements IServiceHandler {
 
                } catch (RepositoryException e) {
                        throw new SlcException("Unexpected error while "
-                                       + "getting repoNode info for repoNode at path "
-                                       + repoNodePath, e);
+                                       + "opening file node " + fileNode, e);
                } finally {
                        JcrUtils.logoutQuietly(businessSession);
                }
        }
-
-       /* DEPENDENCY INJECTION */
-       public void setRepoService(RepoService repoService) {
-               this.repoService = repoService;
-       }
-
 }
\ No newline at end of file
index 21ed8eb088844b64498bc11317c2f3965629031f..48a0d82979020b8c0338cd7c478cbca1b4d2b6fc 100644 (file)
 package org.argeo.slc.client.ui.specific;
 
 import java.net.URL;
+import java.util.UUID;
 
+import javax.jcr.Node;
 import javax.jcr.Session;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jcr.JcrUtils;
+import org.argeo.slc.SlcException;
 import org.argeo.slc.client.rap.OpenJcrFileService;
 import org.argeo.slc.client.rap.SlcRapPlugin;
 import org.argeo.slc.repo.RepoService;
@@ -30,27 +30,30 @@ import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.rwt.RWT;
 import org.eclipse.rwt.service.IServiceHandler;
+import org.eclipse.rwt.service.IServiceManager;
 import org.eclipse.ui.PlatformUI;
 
 /**
  * Rap specific command handler to open a file retrieved from a distant JCR
- * Repository. It forwards the request to the correct service after encoding
- * file name and path in the request URI.
+ * Repository. It creates and register a service instantiated with the
+ * corresponding JCR node, forwards the request, and un register the service on
+ * dispose
  * 
  * This command and the corresponding service are specific for RAP version [1.3,
  * 2)
  */
 public class OpenJcrFile extends AbstractHandler {
-       private final static Log log = LogFactory.getLog(OpenJcrFile.class);
+       // private final static Log log = LogFactory.getLog(OpenJcrFile.class);
 
        public final static String ID = SlcRapPlugin.PLUGIN_ID + ".openJcrFile";
 
-       public final static String PARAM_REPO_NODE_PATH = OpenJcrFileService.PARAM_REPO_NODE_PATH;
-       public final static String PARAM_REPO_URI = OpenJcrFileService.PARAM_REPO_URI;
-       public final static String PARAM_WORKSPACE_NAME = OpenJcrFileService.PARAM_WORKSPACE_NAME;
-       public final static String PARAM_FILE_PATH = OpenJcrFileService.PARAM_FILE_PATH;
+       public final static String PARAM_REPO_NODE_PATH = "param.repoNodePath";
+       public final static String PARAM_REPO_URI = "param.repoUri";
+       public final static String PARAM_WORKSPACE_NAME = "param.workspaceName";
+       public final static String PARAM_FILE_PATH = "param.filePath";
 
        private RepoService repoService;
+       private String currentServiceId;
 
        public Object execute(ExecutionEvent event) throws ExecutionException {
 
@@ -59,74 +62,55 @@ public class OpenJcrFile extends AbstractHandler {
                String wkspName = event.getParameter(PARAM_WORKSPACE_NAME);
                String filePath = event.getParameter(PARAM_FILE_PATH);
 
-               // TODO remove
-               Session session = repoService.getRemoteSession(repoNodePath, repoUri,
-                               wkspName);
-               JcrUtils.logoutQuietly(session);
-
                // TODO sanity check
                if (filePath == null || "".equals(filePath.trim()))
                        return null;
-
+               Session businessSession = null;
                try {
-                       if (log.isDebugEnabled())
-                               log.debug("URL : "
-                                               + createFullDownloadUrl(repoNodePath, repoUri,
-                                                               wkspName, filePath));
-                       // RWT.getResponse().sendRedirect(createFullDownloadUrl(repoNodePath,
-                       // repoUri,
-                       // wkspName, filePath));
-
-                       URL url = new URL(createFullDownloadUrl(repoNodePath, repoUri,
-                                       wkspName, filePath));
+                       businessSession = repoService.getRemoteSession(repoNodePath,
+                                       repoUri, wkspName);
+                       Node result = businessSession.getNode(filePath);
+
+                       // Create a temporary service. No better solution has been found
+                       // yet.
+                       currentServiceId = UUID.randomUUID().toString();
+                       OpenJcrFileService ojfs = new OpenJcrFileService(result);
+                       IServiceManager manager = RWT.getServiceManager();
+                       manager.registerServiceHandler(currentServiceId, ojfs);
+                       String urlStr = createFullDownloadUrl(currentServiceId);
+                       URL url = new URL(urlStr);
                        PlatformUI.getWorkbench().getBrowserSupport()
                                        .createBrowser("DownloadDialog").openURL(url);
                } catch (Exception e) {
-                       e.printStackTrace();
+                       throw new SlcException("Unable to open Jcr File for path "
+                                       + filePath, e);
                }
 
                return null;
        }
 
-       private String createFullDownloadUrl(String repoNodePath, String repoUri,
-                       String wkspName, String filePath) {
+       @Override
+       public void dispose() {
+               IServiceManager manager = RWT.getServiceManager();
+               manager.unregisterServiceHandler(currentServiceId);
+               super.dispose();
+       }
+
+       private String createFullDownloadUrl(String serviceId) {
                StringBuilder url = new StringBuilder();
                url.append(RWT.getRequest().getRequestURL());
 
                StringBuilder params = new StringBuilder();
                params.append("?");
                params.append(IServiceHandler.REQUEST_PARAM).append("=");
-               params.append(OpenJcrFileService.ID);
-               if (repoNodePath != null)
-                       params.append("&").append(OpenJcrFileService.PARAM_REPO_NODE_PATH)
-                                       .append("=").append(repoNodePath);
-
-               if (repoUri != null)
-                       params.append("&").append(OpenJcrFileService.PARAM_REPO_URI)
-                                       .append("=").append(repoUri);
-
-               if (wkspName != null)
-                       params.append("&").append(OpenJcrFileService.PARAM_WORKSPACE_NAME)
-                                       .append("=").append(wkspName);
-
-               if (filePath != null)
-                       params.append("&").append(OpenJcrFileService.PARAM_FILE_PATH)
-                                       .append("=").append(filePath);
-
+               params.append(serviceId);
                String encodedURL = RWT.getResponse().encodeURL(params.toString());
                url.append(encodedURL);
-
                return url.toString();
        }
 
        /* Dependency Injection */
-       // only used as a workaround to force the service instantiation
-       public void setOpenJcrFileService(OpenJcrFileService openJcrFileService) {
-               // do nothing.
-       }
-
        public void setRepoService(RepoService repoService) {
                this.repoService = repoService;
        }
-
 }
\ No newline at end of file
index b63d91c6114bbc7a3191a0e1b9ae484cefaf3957..bcf6070cbfecbf12c79902ea066bdf5b5770452d 100644 (file)
@@ -35,6 +35,7 @@ import org.eclipse.jface.dialogs.IMessageProvider;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Text;
@@ -123,7 +124,7 @@ public class BundleDetailPage extends FormPage implements SlcNames {
                                                        .getProperty(SlcNames.SLC_ARTIFACT_VERSION)
                                                        .getString() : "");
 
-                       // 2nd Line: Vendor, licence, sources
+                       // 3rd Line: Vendor, licence, sources
                        createLT(
                                        parent,
                                        "Vendor",
@@ -131,8 +132,17 @@ public class BundleDetailPage extends FormPage implements SlcNames {
                                                        .getProperty(DistConstants.SLC_BUNDLE_VENDOR)
                                                        .getString() : "N/A");
 
-                       createHyperlink(parent, "Licence", DistConstants.SLC_BUNDLE_LICENCE);
+                       createLicencesLink(parent, "Licence",
+                                       DistConstants.SLC_BUNDLE_LICENCE);
                        addSourceLink(parent);
+
+                       // 2nd Line: The Jar itself and the Manifest
+                       createJarLink(parent);
+                       createManifestLink(parent);
+
+                       // Last line
+                       createPomLink(parent);
+
                } catch (RepositoryException re) {
                        throw new SlcException("Unable to get bundle name for node "
                                        + bundle, re);
@@ -148,8 +158,7 @@ public class BundleDetailPage extends FormPage implements SlcNames {
                Section section = tk.createSection(parent, Section.TITLE_BAR
                                | Section.DESCRIPTION);
                section.setText("Maven");
-               section.setDescription("Copy Paste the below snippet "
-                               + "to use it in your code.");
+               section.setDescription("Add the below tag to your Artifact pom dependencies");
                section.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                Text snippetTxt = createMavenSnippet(section);
                section.setClient(snippetTxt);
@@ -170,6 +179,124 @@ public class BundleDetailPage extends FormPage implements SlcNames {
                return text;
        }
 
+       private void createLicencesLink(Composite parent, String label,
+                       String jcrPropName) throws RepositoryException {
+               tk.createLabel(parent, label, SWT.NONE);
+               if (bundle.hasProperty(jcrPropName)) {
+
+                       String licenceLinkVal = bundle.getProperty(jcrPropName).getString();
+                       String[] licenceVals = licenceLinkVal.split(", ");
+
+                       Composite body = tk.createComposite(parent);
+                       body.setLayout(new RowLayout(SWT.WRAP));
+
+                       for (String value : licenceVals) {
+                               final Hyperlink link = tk
+                                               .createHyperlink(body, value, SWT.NONE);
+                               link.addHyperlinkListener(new AbstractHyperlinkListener() {
+                                       @Override
+                                       public void linkActivated(HyperlinkEvent e) {
+                                               try {
+                                                       IWorkbenchBrowserSupport browserSupport = PlatformUI
+                                                                       .getWorkbench().getBrowserSupport();
+                                                       IWebBrowser browser = browserSupport
+                                                                       .createBrowser(
+                                                                                       IWorkbenchBrowserSupport.LOCATION_BAR
+                                                                                                       | IWorkbenchBrowserSupport.NAVIGATION_BAR,
+                                                                                       "SLC Distribution browser",
+                                                                                       "SLC Distribution browser",
+                                                                                       "A tool tip");
+                                                       browser.openURL(new URL(link.getText()));
+                                               } catch (Exception ex) {
+                                                       throw new SlcException("error opening browser", ex); //$NON-NLS-1$
+                                               }
+                                       }
+                               });
+                       }
+               } else
+                       tk.createLabel(parent, "N/A", SWT.NONE);
+       }
+
+       private void createJarLink(Composite parent) throws RepositoryException {
+               Label label = tk.createLabel(parent, "Jar", SWT.RIGHT);
+               label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+
+               Composite body = tk.createComposite(parent);
+               RowLayout rl = new RowLayout(SWT.HORIZONTAL);
+               rl.spacing = 6;
+               body.setLayout(rl);
+               body.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
+
+               Hyperlink jarLink = tk
+                               .createHyperlink(body, bundle.getName(), SWT.NONE);
+               jarLink.addHyperlinkListener(new OpenFileLinkListener(bundle.getPath()));
+
+               // Corresponding check sums
+
+               String name = bundle.getName() + ".md5";
+               if (bundle.getParent().hasNode(name)) {
+                       Node md5 = bundle.getParent().getNode(name);
+                       Hyperlink md5Link = tk.createHyperlink(body, "MD5", SWT.NONE);
+                       md5Link.addHyperlinkListener(new OpenFileLinkListener(md5.getPath()));
+               }
+
+               name = bundle.getName() + ".sha1";
+               if (bundle.getParent().hasNode(name)) {
+                       Node sha1 = bundle.getParent().getNode(name);
+                       Hyperlink sha1Link = tk.createHyperlink(body, "SHA1", SWT.NONE);
+                       sha1Link.addHyperlinkListener(new OpenFileLinkListener(sha1
+                                       .getPath()));
+               }
+       }
+
+       private void createPomLink(Composite parent) throws RepositoryException {
+               Label label = tk.createLabel(parent, "Pom", SWT.RIGHT);
+               label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
+
+               String name = bundle.getName().substring(0, bundle.getName().length() - "jar".length()) + "pom";
+
+               if (bundle.getParent().hasNode(name)) {
+                       Node pom = bundle.getParent().getNode(name);
+
+                       Composite body = tk.createComposite(parent);
+                       RowLayout rl = new RowLayout(SWT.HORIZONTAL);
+                       rl.spacing = 6;
+                       body.setLayout(rl);
+                       body.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
+                                       3, 1));
+
+                       Hyperlink pomLink = tk.createHyperlink(body, "pom.xml", SWT.NONE);
+                       pomLink.addHyperlinkListener(new OpenFileLinkListener(pom.getPath()));
+
+                       // Corresponding check sums
+
+                       name = pom.getName() + ".md5";
+                       if (pom.getParent().hasNode(name)) {
+                               Node md5 = pom.getParent().getNode(name);
+                               Hyperlink md5Link = tk.createHyperlink(body, "MD5", SWT.NONE);
+                               md5Link.addHyperlinkListener(new OpenFileLinkListener(md5
+                                               .getPath()));
+                       }
+
+                       name = pom.getName() + ".sha1";
+                       if (pom.getParent().hasNode(name)) {
+                               Node sha1 = pom.getParent().getNode(name);
+                               Hyperlink sha1Link = tk.createHyperlink(body, "SHA1", SWT.NONE);
+                               sha1Link.addHyperlinkListener(new OpenFileLinkListener(sha1
+                                               .getPath()));
+                       }
+               } else
+                       tk.createLabel(parent, "N/A", SWT.NONE);
+       }
+
+       private void createManifestLink(Composite parent)
+                       throws RepositoryException {
+               tk.createLabel(parent, "Manifest", SWT.NONE);
+               Hyperlink link = tk.createHyperlink(parent, "MANIFEST.MF", SWT.NONE);
+               // link.addHyperlinkListener(new
+               // OpenFileLinkListener(bundle.getPath()));
+       }
+
        private void createHyperlink(Composite parent, String label,
                        String jcrPropName) throws RepositoryException {
                tk.createLabel(parent, label, SWT.NONE);
@@ -220,26 +347,30 @@ public class BundleDetailPage extends FormPage implements SlcNames {
                                                false));
                                final Hyperlink link = tk.createHyperlink(parent, srcName,
                                                SWT.NONE);
-                               link.addHyperlinkListener(new AbstractHyperlinkListener() {
-                                       @Override
-                                       public void linkActivated(HyperlinkEvent e) {
-                                               try {
-                                                       ModuleEditorInput editorInput = (ModuleEditorInput) getEditorInput();
-                                                       Map<String, String> params = new HashMap<String, String>();
-                                                       params.put(OpenJcrFile.PARAM_REPO_NODE_PATH,
-                                                                       editorInput.getRepoNodePath());
-                                                       params.put(OpenJcrFile.PARAM_REPO_URI,
-                                                                       editorInput.getUri());
-                                                       params.put(OpenJcrFile.PARAM_WORKSPACE_NAME,
-                                                                       editorInput.getWorkspaceName());
-                                                       params.put(OpenJcrFile.PARAM_FILE_PATH,
-                                                                       sourcesNode.getPath());
-                                                       CommandUtils.callCommand(OpenJcrFile.ID, params);
-                                               } catch (Exception ex) {
-                                                       throw new SlcException("error opening browser", ex); //$NON-NLS-1$
-                                               }
-                                       }
-                               });
+                               link.addHyperlinkListener(new OpenFileLinkListener(sourcesNode
+                                               .getPath()));
+
+                               // {
+                               // @Override
+                               // public void linkActivated(HyperlinkEvent e) {
+                               // try {
+                               // ModuleEditorInput editorInput = (ModuleEditorInput)
+                               // getEditorInput();
+                               // Map<String, String> params = new HashMap<String, String>();
+                               // params.put(OpenJcrFile.PARAM_REPO_NODE_PATH,
+                               // editorInput.getRepoNodePath());
+                               // params.put(OpenJcrFile.PARAM_REPO_URI,
+                               // editorInput.getUri());
+                               // params.put(OpenJcrFile.PARAM_WORKSPACE_NAME,
+                               // editorInput.getWorkspaceName());
+                               // params.put(OpenJcrFile.PARAM_FILE_PATH,
+                               // );
+                               // CommandUtils.callCommand(OpenJcrFile.ID, params);
+                               // } catch (Exception ex) {
+                               //                                                      throw new SlcException("error opening browser", ex); //$NON-NLS-1$
+                               // }
+                               // }
+                               // });
 
                        }
                } catch (RepositoryException e) {
@@ -248,6 +379,31 @@ public class BundleDetailPage extends FormPage implements SlcNames {
                }
        }
 
+       private class OpenFileLinkListener extends AbstractHyperlinkListener {
+               final private String path;
+
+               public OpenFileLinkListener(String path) {
+                       this.path = path;
+               }
+
+               @Override
+               public void linkActivated(HyperlinkEvent e) {
+                       try {
+                               ModuleEditorInput editorInput = (ModuleEditorInput) getEditorInput();
+                               Map<String, String> params = new HashMap<String, String>();
+                               params.put(OpenJcrFile.PARAM_REPO_NODE_PATH,
+                                               editorInput.getRepoNodePath());
+                               params.put(OpenJcrFile.PARAM_REPO_URI, editorInput.getUri());
+                               params.put(OpenJcrFile.PARAM_WORKSPACE_NAME,
+                                               editorInput.getWorkspaceName());
+                               params.put(OpenJcrFile.PARAM_FILE_PATH, path);
+                               CommandUtils.callCommand(OpenJcrFile.ID, params);
+                       } catch (Exception ex) {
+                               throw new SlcException("error opening browser", ex); //$NON-NLS-1$
+                       }
+               }
+       }
+
        /** Creates a text area with corresponding maven snippet */
        private Text createMavenSnippet(Composite parent) {
                Text mavenSnippet = new Text(parent, SWT.MULTI | SWT.WRAP);