From bdd0a41aed7edf21ec2a65cfa17a86af2ef8c48a Mon Sep 17 00:00:00 2001 From: dims Date: Tue, 17 Jun 2008 00:23:01 +0000 Subject: Move Tuscany from Incubator to top level. git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@668359 13f79535-47bb-0310-9956-ffa450edef68 --- java/sca/distribution/webapp/pom.xml | 350 +++++++++++++++++++++ .../sca/webapp/ContributionUploaderServlet.java | 116 +++++++ .../tuscany/sca/webapp/WarContextListener.java | 304 ++++++++++++++++++ .../webapp/src/main/webapp/WEB-INF/web.xml | 66 ++++ .../main/webapp/sca-contributions/safeToDelete.tmp | 1 + .../sca-contributions/sample-calculator-nodeB.jar | Bin 0 -> 4593 bytes .../sample-helloworld-ws-service.jar | Bin 0 -> 7065 bytes .../webapp/src/main/webapp/scaDomainInfo.jsp | 74 +++++ 8 files changed, 911 insertions(+) create mode 100644 java/sca/distribution/webapp/pom.xml create mode 100644 java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/ContributionUploaderServlet.java create mode 100644 java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/WarContextListener.java create mode 100644 java/sca/distribution/webapp/src/main/webapp/WEB-INF/web.xml create mode 100644 java/sca/distribution/webapp/src/main/webapp/sca-contributions/safeToDelete.tmp create mode 100644 java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-calculator-nodeB.jar create mode 100644 java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-helloworld-ws-service.jar create mode 100644 java/sca/distribution/webapp/src/main/webapp/scaDomainInfo.jsp (limited to 'java/sca/distribution/webapp') diff --git a/java/sca/distribution/webapp/pom.xml b/java/sca/distribution/webapp/pom.xml new file mode 100644 index 0000000000..695eba74c8 --- /dev/null +++ b/java/sca/distribution/webapp/pom.xml @@ -0,0 +1,350 @@ + + + + 4.0.0 + + org.apache.tuscany.sca + tuscany-sca + 1.4-SNAPSHOT + ../../pom.xml + + + Apache Tuscany SCA WebApp Runtime + tuscany-webapp + war + + + + ${pom.groupId} + tuscany-assembly + ${pom.version} + + + ${pom.groupId} + tuscany-assembly-xml + ${pom.version} + + + ${pom.groupId} + tuscany-binding-dwr + ${pom.version} + + + ${pom.groupId} + tuscany-binding-ejb + ${pom.version} + + + ${pom.groupId} + tuscany-binding-feed + ${pom.version} + + + ${pom.groupId} + tuscany-binding-http + ${pom.version} + + + ${pom.groupId} + tuscany-binding-jsonrpc + ${pom.version} + + + ${pom.groupId} + tuscany-binding-notification + ${pom.version} + + + ${pom.groupId} + tuscany-binding-rmi + ${pom.version} + + + ${pom.groupId} + tuscany-binding-sca + ${pom.version} + + + ${pom.groupId} + tuscany-binding-sca-xml + ${pom.version} + + + ${pom.groupId} + tuscany-binding-ws + ${pom.version} + + + ${pom.groupId} + tuscany-binding-ws-axis2 + ${pom.version} + + + javax.servlet + servlet-api + + + junit + junit + + + + + ${pom.groupId} + tuscany-binding-ws-xml + ${pom.version} + + + ${pom.groupId} + tuscany-contribution + ${pom.version} + + + ${pom.groupId} + tuscany-contribution-impl + ${pom.version} + + + ${pom.groupId} + tuscany-contribution-java + ${pom.version} + + + ${pom.groupId} + tuscany-contribution-namespace + ${pom.version} + + + ${pom.groupId} + tuscany-core + ${pom.version} + + + ${pom.groupId} + tuscany-core-databinding + ${pom.version} + + + ${pom.groupId} + tuscany-core-spi + ${pom.version} + + + ${pom.groupId} + tuscany-core-spring + ${pom.version} + + + ${pom.groupId} + tuscany-databinding + ${pom.version} + + + ${pom.groupId} + tuscany-databinding-axiom + ${pom.version} + + + ${pom.groupId} + tuscany-databinding-jaxb + ${pom.version} + + + ${pom.groupId} + tuscany-databinding-sdo + ${pom.version} + + + ${pom.groupId} + tuscany-databinding-sdo-axiom + ${pom.version} + + + ${pom.groupId} + tuscany-definitions + ${pom.version} + + + ${pom.groupId} + tuscany-definitions-xml + ${pom.version} + + + ${pom.groupId} + tuscany-domain + ${pom.version} + + + ${pom.groupId} + tuscany-domain-api + ${pom.version} + + + ${pom.groupId} + tuscany-domain-impl + ${pom.version} + + + ${pom.groupId} + tuscany-extension-helper + ${pom.version} + + + ${pom.groupId} + tuscany-host-embedded + ${pom.version} + + + ${pom.groupId} + tuscany-host-http + ${pom.version} + + + ${pom.groupId} + tuscany-host-rmi + ${pom.version} + + + ${pom.groupId} + tuscany-host-webapp + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-java + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-java-xml + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-java-runtime + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-node + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-node-runtime + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-notification + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-resource + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-script + ${pom.version} + + + ${pom.groupId} + tuscany-implementation-spring + ${pom.version} + + + ${pom.groupId} + tuscany-interface + ${pom.version} + + + ${pom.groupId} + tuscany-interface-java + ${pom.version} + + + ${pom.groupId} + tuscany-interface-java-xml + ${pom.version} + + + ${pom.groupId} + tuscany-interface-wsdl + ${pom.version} + + + ${pom.groupId} + tuscany-interface-wsdl-xml + ${pom.version} + + + ${pom.groupId} + tuscany-node + ${pom.version} + + + ${pom.groupId} + tuscany-node-api + ${pom.version} + + + ${pom.groupId} + tuscany-node-impl + ${pom.version} + + + ${pom.groupId} + tuscany-policy + ${pom.version} + + + ${pom.groupId} + tuscany-policy-xml + ${pom.version} + + + ${pom.groupId} + tuscany-sca-api + ${pom.version} + + + javax.servlet + servlet-api + 2.3 + provided + + + commons-fileupload + commons-fileupload + 1.1.1 + + + commons-io + commons-io + 1.1 + + + + + tuscany + + diff --git a/java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/ContributionUploaderServlet.java b/java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/ContributionUploaderServlet.java new file mode 100644 index 0000000000..9a0c66a776 --- /dev/null +++ b/java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/ContributionUploaderServlet.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.webapp; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Iterator; +import java.util.List; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; + +import org.apache.tuscany.sca.host.webapp.HotUpdateContextListener; + +/** + * A Servlet to upload a contribution file. + */ +public class ContributionUploaderServlet extends HttpServlet { + + private static final long serialVersionUID = System.currentTimeMillis(); + + private File repository; + + @Override + public void init(ServletConfig config) throws ServletException { + ServletContext servletContext = config.getServletContext(); + repository = new File(servletContext.getRealPath(HotUpdateContextListener.REPOSITORY_FOLDER_NAME)); + } + + @Override + public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { + // Check that we have a file upload request + boolean isMultipart = ServletFileUpload.isMultipartContent(request); + if (!isMultipart) { + throw new RuntimeException("Need multipart content"); + } + + // Create a factory for disk-based file items + FileItemFactory factory = new DiskFileItemFactory(); + + // Create a new file upload handler + ServletFileUpload upload = new ServletFileUpload(factory); + + try { + // Parse the request + List /* FileItem */ items = upload.parseRequest(request); + // Process the uploaded items + Iterator iter = items.iterator(); + while (iter.hasNext()) { + FileItem item = (FileItem) iter.next(); + + if (!item.isFormField()) { + String fileName = item.getName(); + int index = fileName.lastIndexOf("\\") + 1; + String uploadedFileName = repository.getAbsolutePath() + "/" + fileName.substring(index); + File uploadedFile = new File(uploadedFileName); + item.write(uploadedFile); + } + } + } + catch(FileUploadException e) { + throw new RuntimeException(e); + } + catch(Throwable e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + + setResponse(response, request); + } + + private void setResponse(HttpServletResponse response, HttpServletRequest request) throws IOException { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println(""); + out.println(""); + out.println("Apache Tuscany WebApp Runtime"); + out.println(""); + out.println(""); + out.println("

Composite file uploaded

"); + int port = request.getServerPort(); + String portSubStr = ((port == -1) ? "" : (":" + request.getServerPort())); + String backPath = request.getScheme() + "://" + request.getServerName() + portSubStr + request.getContextPath(); + out.println("Go back"); + out.println(""); + out.println(""); + } +} diff --git a/java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/WarContextListener.java b/java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/WarContextListener.java new file mode 100644 index 0000000000..e46cadfa3d --- /dev/null +++ b/java/sca/distribution/webapp/src/main/java/org/apache/tuscany/sca/webapp/WarContextListener.java @@ -0,0 +1,304 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.webapp; + +import java.io.File; +import java.io.FilenameFilter; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import org.apache.tuscany.sca.domain.SCADomain; +import org.apache.tuscany.sca.node.NodeException; +import org.apache.tuscany.sca.node.SCANode; +import org.apache.tuscany.sca.node.SCANodeFactory; + +/** + * A ServletContextListener for the Tuscany WAR distribution. + * + * Starts and stops a Tuscany SCA domain Node for the webapp. + */ +public class WarContextListener implements ServletContextListener { + private final static Logger logger = Logger.getLogger(WarContextListener.class.getName()); + + protected SCANode node; + protected SCADomain domain; + protected AddableURLClassLoader classLoader; + protected File repository; + + protected boolean useHotUpdate; + protected long hotDeployInterval = 2000; // 2 seconds, 0 = no hot deploy + protected Thread hotDeployThread; + protected boolean stopHotDeployThread; + + protected HashMap existingContributions; // value is last modified time + + private String domainName; + private String nodeName; + + protected static final String NODE_ATTRIBUTE = WarContextListener.class.getName() + ".TuscanyNode"; + protected static final String REPOSITORY_FOLDER_NAME = "sca-contributions"; + + public void contextInitialized(ServletContextEvent event) { + ServletContext servletContext = event.getServletContext(); + initParameters(servletContext); + try { + + initNode(); + + } catch (Throwable e) { + e.printStackTrace(); + servletContext.log("exception initializing SCA node", e); + } + } + + public void contextDestroyed(ServletContextEvent event) { + if (node != null) { + stopNode(); + } + } + + protected void stopNode() { + try { + + node.stop(); + logger.log(Level.INFO, "SCA node stopped"); + + } catch (Throwable e) { + e.printStackTrace(); + logger.log(Level.SEVERE, "exception stopping SCA Node", e); + } + } + + protected void initNode() throws NodeException, URISyntaxException { + logger.log(Level.INFO, "SCA node starting"); + + classLoader = new AddableURLClassLoader(new URL[] {}, Thread.currentThread().getContextClassLoader()); + Thread.currentThread().setContextClassLoader(classLoader); + + SCANodeFactory nodeFactory = SCANodeFactory.newInstance(); + node = nodeFactory.createSCANode(nodeName, domainName); + domain = node.getDomain(); + + existingContributions = new HashMap(); + URL[] contributions = getContributionJarURLs(repository); + for (URL contribution : contributions) { + addContribution(contribution); + } + + node.start(); + + initHotDeploy(repository); + } + + protected void addContribution(URL contribution) throws URISyntaxException, NodeException { + classLoader.addURL(contribution); + node.addContribution(contribution.toString(), contribution); + existingContributions.put(contribution, new Long(new File(contribution.toURI()).lastModified())); + logger.log(Level.INFO, "Added contribution: " + contribution); + } + + protected URL[] getContributionJarURLs(File repositoryDir) { + + String[] jarNames = repositoryDir.list(new FilenameFilter() { + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + }}); + + List contributionJars = new ArrayList(); + if (jarNames != null) { + for (String jar : jarNames) { + try { + contributionJars.add(new File(repositoryDir, jar).toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + } + + return contributionJars.toArray(new URL[contributionJars.size()]); + } + + private void initHotDeploy(final File repository) { + + if (hotDeployInterval == 0) { + return; // hotUpdateInterval of 0 disables hotupdate + } + + Runnable runable = new Runnable() { + + public void run() { + logger.info("Contribution hot deploy activated"); + while (!stopHotDeployThread) { + try { + Thread.sleep(hotDeployInterval); + } catch (InterruptedException e) { + } + if (!stopHotDeployThread) { + checkForUpdates(repository); + } + } + logger.info("Tuscany contribution hot deploy stopped"); + } + }; + hotDeployThread = new Thread(runable, "TuscanyHotDeploy"); + stopHotDeployThread = false; + hotDeployThread.start(); + } + + protected void checkForUpdates(File repository) { + URL[] currentContributions = getContributionJarURLs(repository); + + List addedContributions = getAddedContributions(currentContributions); + for (URL contribution : addedContributions) { + try { + addContribution(contribution); + // node.startContribution(contribution.toString()); + } catch (Throwable e) { + e.printStackTrace(); + logger.log(Level.WARNING, "Exception adding contribution: " + e); + } + } + if (addedContributions.size() > 0) { + try { + node.start(); + } catch (NodeException e) { + e.printStackTrace(); + logger.log(Level.WARNING, "Exception restarting node for added contributions: " + e); + } + } + + if (useHotUpdate && areContributionsAltered(currentContributions)) { + stopNode(); + try { + initNode(); + } catch (Throwable e) { + e.printStackTrace(); + logger.log(Level.SEVERE, "exception starting SCA Node", e); + } + } + } + + protected List getAddedContributions(URL[] currentContrabutions) { + List urls = new ArrayList(); + for (URL url : currentContrabutions) { + if (!existingContributions.containsKey(url)) { + urls.add(url); + } + } + return urls; + } + + protected boolean areContributionsAltered(URL[] currentContrabutions) { + try { + + List removedContributions = getRemovedContributions(currentContrabutions); + List updatedContributions = getUpdatedContributions(currentContrabutions); + + return (removedContributions.size() > 0 || updatedContributions.size() > 0); + + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + + protected List getUpdatedContributions(URL[] currentContrabutions) throws URISyntaxException { + List urls = new ArrayList(); + for (URL url : currentContrabutions) { + if (existingContributions.containsKey(url)) { + File curentFile = new File(url.toURI()); + if (curentFile.lastModified() != existingContributions.get(url)) { + urls.add(url); + logger.info("updated contribution: " + curentFile.getName()); + } + } + } + return urls; + } + + protected List getRemovedContributions(URL[] currentContrabutions) throws URISyntaxException { + List currentUrls = Arrays.asList(currentContrabutions); + List urls = new ArrayList(); + for (URL url : existingContributions.keySet()) { + if (!currentUrls.contains(url)) { + urls.add(url); + } + } + for (URL url : urls) { + logger.info("removed contributions: " + new File(url.toURI()).getName()); + } + return urls; + } + + protected void initParameters(ServletContext servletContext) { + if (servletContext.getInitParameter("domainName") != null) { + domainName = servletContext.getInitParameter("domainName"); + } else { + domainName = null; + } + + if (servletContext.getInitParameter("nodeName") != null) { + nodeName = servletContext.getInitParameter("nodeName"); + } else { + nodeName = "DefaultNode"; + } + + if (servletContext.getInitParameter("hotDeployInterval") != null) { + hotDeployInterval = Long.parseLong(servletContext.getInitParameter("hotDeployInterval")); + } + + useHotUpdate = Boolean.valueOf(servletContext.getInitParameter("hotUpdate")).booleanValue(); + + if (servletContext.getInitParameter("repositoryFolder") != null) { + repository = new File(servletContext.getInitParameter("repositoryFolder")); + } else { + repository = new File(servletContext.getRealPath(REPOSITORY_FOLDER_NAME)); + } + logger.info("Tuscany Contribution Repository -> " + repository); + } + +} + +class AddableURLClassLoader extends URLClassLoader { + + public AddableURLClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + /** + * Make URLClassLoader addURL public + */ + @Override + public void addURL(URL url) { + super.addURL(url); + } + +} diff --git a/java/sca/distribution/webapp/src/main/webapp/WEB-INF/web.xml b/java/sca/distribution/webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..8e65a9b96d --- /dev/null +++ b/java/sca/distribution/webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,66 @@ + + + + + + + Apache Tuscany WebApp Runtime + + + org.apache.tuscany.sca.webapp.WarContextListener + + + + repositoryFolder + /TuscanyRepository + + + + + + nodeName + http://localhost:8080/ + + + + hotDeployInterval + 2000 + + + + hotUpdate + no + + + + tuscany + org.apache.tuscany.sca.host.webapp.TuscanyServletFilter + + + + tuscany + /* + + + diff --git a/java/sca/distribution/webapp/src/main/webapp/sca-contributions/safeToDelete.tmp b/java/sca/distribution/webapp/src/main/webapp/sca-contributions/safeToDelete.tmp new file mode 100644 index 0000000000..4efe4b5db3 --- /dev/null +++ b/java/sca/distribution/webapp/src/main/webapp/sca-contributions/safeToDelete.tmp @@ -0,0 +1 @@ +File just to get the sca-contributions folder included in webapp \ No newline at end of file diff --git a/java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-calculator-nodeB.jar b/java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-calculator-nodeB.jar new file mode 100644 index 0000000000..0df7ab8d74 Binary files /dev/null and b/java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-calculator-nodeB.jar differ diff --git a/java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-helloworld-ws-service.jar b/java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-helloworld-ws-service.jar new file mode 100644 index 0000000000..acf47451b3 Binary files /dev/null and b/java/sca/distribution/webapp/src/main/webapp/sca-contributions/sample-helloworld-ws-service.jar differ diff --git a/java/sca/distribution/webapp/src/main/webapp/scaDomainInfo.jsp b/java/sca/distribution/webapp/src/main/webapp/scaDomainInfo.jsp new file mode 100644 index 0000000000..c92c164cfb --- /dev/null +++ b/java/sca/distribution/webapp/src/main/webapp/scaDomainInfo.jsp @@ -0,0 +1,74 @@ +<%-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--%> + +<%@ page import="org.apache.tuscany.sca.host.embedded.SCADomain"%> +<%@ page import="org.apache.tuscany.sca.host.embedded.management.ComponentManager"%> +<%@ page import="org.apache.tuscany.sca.assembly.ComponentService"%> +<%@ page import="org.apache.tuscany.sca.assembly.Binding"%> + +<%@ page contentType="text/html;charset=UTF-8" language="java" %> +<% + SCADomain scaDomain = (SCADomain) application.getAttribute("org.apache.tuscany.sca.SCADomain"); + ComponentManager componentManager = scaDomain.getComponentManager(); +%> + +Apache Tuscany WebApp Runtime + + +Apache Tuscany WebApp Runtime +
+Components in SCA Domain: + <% + java.util.Iterator i = componentManager.getComponentNames().iterator(); + while (i.hasNext()) { + String compName = i.next().toString(); + + %>
<%=compName%>
<% + + org.apache.tuscany.sca.assembly.Component comp = componentManager.getComponent(compName); + java.util.Iterator j = comp.getServices().iterator(); + while (j.hasNext()) { + ComponentService compService = (ComponentService)j.next(); + + %><%=" - Service: " + compService.getName()%>
<% + + java.util.Iterator k = compService.getBindings().iterator(); + while (k.hasNext()) { + Binding b = (Binding)k.next(); + String bindingType = b.getClass().getName(); + + %><%="-- Binding: " + b.getName() + "(" + bindingType.substring(bindingType.lastIndexOf('.')+1) + ") URI: " + b.getURI()%>
<% + } + } + } + %> +
+ +
+You can fill in a composite file to upload + +
+Composite file to upload:
+
+ to upload the composite file +
+ + + + -- cgit v1.2.3