diff options
| author | Kim van der Riet <kpvdr@apache.org> | 2013-09-20 18:59:30 +0000 |
|---|---|---|
| committer | Kim van der Riet <kpvdr@apache.org> | 2013-09-20 18:59:30 +0000 |
| commit | c70bf3ea28cdf6bafd8571690d3e5c466a0658a2 (patch) | |
| tree | 68b24940e433f3f9c278b054d9ea1622389bd332 /qpid/java/broker-plugins/management-http | |
| parent | fcdf1723c7b5cdf0772054a93edb6e7d97c4bb1e (diff) | |
| download | qpid-python-c70bf3ea28cdf6bafd8571690d3e5c466a0658a2.tar.gz | |
QPID-4984: WIP - Merge from trunk r.1525056
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/linearstore@1525101 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/broker-plugins/management-http')
66 files changed, 4452 insertions, 185 deletions
diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java index d87a1755da..c6623aefcf 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java @@ -20,9 +20,9 @@ */ package org.apache.qpid.server.management.plugin; -import java.io.File; import java.lang.reflect.Type; import java.net.SocketAddress; +import java.security.GeneralSecurityException; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.Map; import java.util.UUID; +import javax.net.ssl.SSLContext; import org.apache.log4j.Logger; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.logging.actors.CurrentActor; @@ -39,11 +40,15 @@ import org.apache.qpid.server.management.plugin.filter.ForbiddingAuthorisationFi import org.apache.qpid.server.management.plugin.filter.RedirectingAuthorisationFilter; import org.apache.qpid.server.management.plugin.servlet.DefinedFileServlet; import org.apache.qpid.server.management.plugin.servlet.FileServlet; +import org.apache.qpid.server.management.plugin.servlet.LogFileServlet; import org.apache.qpid.server.management.plugin.servlet.rest.HelperServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.LogFileListingServlet; import org.apache.qpid.server.management.plugin.servlet.rest.LogRecordsServlet; import org.apache.qpid.server.management.plugin.servlet.rest.LogoutServlet; import org.apache.qpid.server.management.plugin.servlet.rest.MessageContentServlet; import org.apache.qpid.server.management.plugin.servlet.rest.MessageServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.PreferencesServlet; +import org.apache.qpid.server.management.plugin.servlet.rest.UserPreferencesServlet; import org.apache.qpid.server.management.plugin.servlet.rest.RestServlet; import org.apache.qpid.server.management.plugin.servlet.rest.SaslServlet; import org.apache.qpid.server.management.plugin.servlet.rest.StructureServlet; @@ -60,6 +65,7 @@ import org.apache.qpid.server.model.GroupProvider; import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.Plugin; import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.PreferencesProvider; import org.apache.qpid.server.model.Protocol; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.Session; @@ -70,7 +76,6 @@ import org.apache.qpid.server.model.User; import org.apache.qpid.server.model.VirtualHost; import org.apache.qpid.server.model.adapter.AbstractPluginAdapter; import org.apache.qpid.server.plugin.PluginFactory; -import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.util.MapValueConverter; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.DispatcherType; @@ -238,13 +243,17 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem { throw new IllegalConfigurationException("Key store is not configured. Cannot start management on HTTPS port without keystore"); } - String keyStorePath = (String)keyStore.getAttribute(KeyStore.PATH); - String keyStorePassword = keyStore.getPassword(); - SslContextFactory factory = new SslContextFactory(); - factory.setKeyStorePath(keyStorePath); - factory.setKeyStorePassword(keyStorePassword); - + try + { + SSLContext sslContext = SSLContext.getInstance("TLS"); + sslContext.init(keyStore.getKeyManagers(), null, null); + factory.setSslContext(sslContext); + } + catch (GeneralSecurityException e) + { + throw new RuntimeException("Cannot configure port " + port.getName() + " for transport " + Transport.SSL, e); + } connector = new SslSocketConnector(factory); } else @@ -288,7 +297,10 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem addRestServlet(root, "keystore", KeyStore.class); addRestServlet(root, "truststore", TrustStore.class); addRestServlet(root, "plugin", Plugin.class); + addRestServlet(root, "preferencesprovider", AuthenticationProvider.class, PreferencesProvider.class); + root.addServlet(new ServletHolder(new UserPreferencesServlet()), "/rest/userpreferences/*"); + root.addServlet(new ServletHolder(new PreferencesServlet()), "/rest/preferences"); root.addServlet(new ServletHolder(new StructureServlet()), "/rest/structure"); root.addServlet(new ServletHolder(new MessageServlet()), "/rest/message/*"); root.addServlet(new ServletHolder(new MessageContentServlet()), "/rest/message-content/*"); @@ -312,6 +324,15 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.txt"); root.addServlet(new ServletHolder(FileServlet.INSTANCE), "*.xsl"); root.addServlet(new ServletHolder(new HelperServlet()), "/rest/helper"); + root.addServlet(new ServletHolder(new LogFileListingServlet()), "/rest/logfiles"); + root.addServlet(new ServletHolder(new LogFileServlet()), "/rest/logfile"); + + String[] timeZoneFiles = {"africa", "antarctica", "asia", "australasia", "backward", + "etcetera", "europe", "northamerica", "pacificnew", "southamerica"}; + for (String timeZoneFile : timeZoneFiles) + { + root.addServlet(new ServletHolder(FileServlet.INSTANCE), "/dojo/dojox/date/zoneinfo/" + timeZoneFile); + } final SessionManager sessionManager = root.getSessionHandler().getSessionManager(); sessionManager.setSessionCookie(JSESSIONID_COOKIE_PREFIX + lastPort); @@ -407,9 +428,9 @@ public class HttpManagement extends AbstractPluginAdapter implements HttpManagem } @Override - public SubjectCreator getSubjectCreator(SocketAddress localAddress) + public AuthenticationProvider getAuthenticationProvider(SocketAddress localAddress) { - return getBroker().getSubjectCreator(localAddress); + return getBroker().getAuthenticationProvider(localAddress); } @Override diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java index 56919e2e6b..7d89daa427 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java @@ -22,7 +22,7 @@ package org.apache.qpid.server.management.plugin; import java.net.SocketAddress; -import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.model.AuthenticationProvider; public interface HttpManagementConfiguration { @@ -34,5 +34,5 @@ public interface HttpManagementConfiguration boolean isHttpBasicAuthenticationEnabled(); - SubjectCreator getSubjectCreator(SocketAddress localAddress); + AuthenticationProvider getAuthenticationProvider(SocketAddress localAddress); } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java index 4c6e5bf63e..990ff1c53b 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementUtil.java @@ -168,7 +168,7 @@ public class HttpManagementUtil { Subject subject = null; SocketAddress localAddress = getSocketAddress(request); - SubjectCreator subjectCreator = managementConfig.getSubjectCreator(localAddress); + SubjectCreator subjectCreator = managementConfig.getAuthenticationProvider(localAddress).getSubjectCreator(); String remoteUser = request.getRemoteUser(); if (remoteUser != null || subjectCreator.isAnonymousAuthenticationAllowed()) diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/log/LogFileDetails.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/log/LogFileDetails.java new file mode 100644 index 0000000000..09dabd0e73 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/log/LogFileDetails.java @@ -0,0 +1,78 @@ +/* + * 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.qpid.server.management.plugin.log; + +import java.io.File; + +public class LogFileDetails +{ + private String _name; + private File _location; + private String _mimeType; + private long _size; + private long _lastModified; + private String _appenderName; + + public LogFileDetails(String name, String appenderName, File location, String mimeType, long fileSize, long lastUpdateTime) + { + super(); + _name = name; + _location = location; + _mimeType = mimeType; + _size = fileSize; + _lastModified = lastUpdateTime; + _appenderName = appenderName; + } + + public String getName() + { + return _name; + } + + public File getLocation() + { + return _location; + } + + public String getMimeType() + { + return _mimeType; + } + + public long getSize() + { + return _size; + } + + public long getLastModified() + { + return _lastModified; + } + + public String getAppenderName() + { + return _appenderName; + } + + @Override + public String toString() + { + return "LogFileDetails [name=" + _name + "]"; + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/log/LogFileHelper.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/log/LogFileHelper.java new file mode 100644 index 0000000000..03d98d020b --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/log/LogFileHelper.java @@ -0,0 +1,228 @@ +/* + * 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.qpid.server.management.plugin.log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.log4j.Appender; +import org.apache.log4j.FileAppender; +import org.apache.log4j.QpidCompositeRollingAppender; + +public class LogFileHelper +{ + public static final String GZIP_MIME_TYPE = "application/x-gzip"; + public static final String TEXT_MIME_TYPE = "text/plain"; + public static final String ZIP_MIME_TYPE = "application/zip"; + public static final String GZIP_EXTENSION = ".gz"; + private static final int BUFFER_LENGTH = 1024 * 4; + private Collection<Appender> _appenders; + + public LogFileHelper(Collection<Appender> appenders) + { + super(); + _appenders = appenders; + } + + public List<LogFileDetails> findLogFileDetails(String[] requestedFiles) + { + List<LogFileDetails> logFiles = new ArrayList<LogFileDetails>(); + Map<String, List<LogFileDetails>> cache = new HashMap<String, List<LogFileDetails>>(); + for (int i = 0; i < requestedFiles.length; i++) + { + String[] paths = requestedFiles[i].split("/"); + if (paths.length != 2) + { + throw new IllegalArgumentException("Log file name '" + requestedFiles[i] + "' does not include an appender name"); + } + + String appenderName = paths[0]; + String fileName = paths[1]; + + List<LogFileDetails> appenderFiles = cache.get(appenderName); + if (appenderFiles == null) + { + Appender fileAppender = null; + for (Appender appender : _appenders) + { + if (appenderName.equals(appender.getName())) + { + fileAppender = appender; + break; + } + } + if (fileAppender == null) + { + continue; + } + appenderFiles = getAppenderFiles(fileAppender, true); + if (appenderFiles == null) + { + continue; + } + cache.put(appenderName, appenderFiles); + } + for (LogFileDetails logFileDetails : appenderFiles) + { + if (logFileDetails.getName().equals(fileName)) + { + logFiles.add(logFileDetails); + } + } + } + return logFiles; + } + + public List<LogFileDetails> getLogFileDetails(boolean includeLogFileLocation) + { + List<LogFileDetails> results = new ArrayList<LogFileDetails>(); + for (Appender appender : _appenders) + { + List<LogFileDetails> appenderFiles = getAppenderFiles(appender, includeLogFileLocation); + if (appenderFiles != null) + { + results.addAll(appenderFiles); + } + } + return results; + } + + public void writeLogFiles(List<LogFileDetails> logFiles, OutputStream os) throws IOException + { + ZipOutputStream out = new ZipOutputStream(os); + try + { + addLogFileEntries(logFiles, out); + } + finally + { + out.close(); + } + } + + public void writeLogFile(File file, OutputStream os) throws IOException + { + FileInputStream fis = new FileInputStream(file); + try + { + byte[] bytes = new byte[BUFFER_LENGTH]; + int length = 1; + while ((length = fis.read(bytes)) != -1) + { + os.write(bytes, 0, length); + } + } + finally + { + fis.close(); + } + } + + private List<LogFileDetails> getAppenderFiles(Appender appender, boolean includeLogFileLocation) + { + if (appender instanceof QpidCompositeRollingAppender) + { + return listAppenderFiles((QpidCompositeRollingAppender) appender, includeLogFileLocation); + } + else if (appender instanceof FileAppender) + { + return listAppenderFiles((FileAppender) appender, includeLogFileLocation); + } + return null; + } + + private List<LogFileDetails> listAppenderFiles(FileAppender appender, boolean includeLogFileLocation) + { + String appenderFilePath = appender.getFile(); + File appenderFile = new File(appenderFilePath); + if (appenderFile.exists()) + { + return listLogFiles(appenderFile.getParentFile(), appenderFile.getName(), appender.getName(), "", includeLogFileLocation); + } + return Collections.emptyList(); + } + + private List<LogFileDetails> listAppenderFiles(QpidCompositeRollingAppender appender, boolean includeLogFileLocation) + { + List<LogFileDetails> files = listAppenderFiles((FileAppender) appender, includeLogFileLocation); + String appenderFilePath = appender.getFile(); + File appenderFile = new File(appenderFilePath); + File backupFolder = new File(appender.getBackupFilesToPath()); + if (backupFolder.exists()) + { + String backFolderName = backupFolder.getName() + "/"; + List<LogFileDetails> backedUpFiles = listLogFiles(backupFolder, appenderFile.getName(), appender.getName(), + backFolderName, includeLogFileLocation); + files.addAll(backedUpFiles); + } + return files; + } + + private List<LogFileDetails> listLogFiles(File parent, String baseFileName, String appenderName, String relativePath, + boolean includeLogFileLocation) + { + List<LogFileDetails> files = new ArrayList<LogFileDetails>(); + for (File file : parent.listFiles()) + { + String name = file.getName(); + if (name.startsWith(baseFileName)) + { + files.add(new LogFileDetails(name, appenderName, includeLogFileLocation ? file : null, getMimeType(name), file.length(), + file.lastModified())); + } + } + return files; + } + + private String getMimeType(String fileName) + { + if (fileName.endsWith(GZIP_EXTENSION)) + { + return GZIP_MIME_TYPE; + } + return TEXT_MIME_TYPE; + } + + private void addLogFileEntries(List<LogFileDetails> files, ZipOutputStream out) throws IOException + { + for (LogFileDetails logFileDetails : files) + { + File file = logFileDetails.getLocation(); + if (file.exists()) + { + ZipEntry entry = new ZipEntry(logFileDetails.getAppenderName() + "/" + logFileDetails.getName()); + entry.setSize(file.length()); + out.putNextEntry(entry); + writeLogFile(file, out); + out.closeEntry(); + } + out.flush(); + } + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/LogFileServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/LogFileServlet.java new file mode 100644 index 0000000000..1fa03dc3dc --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/LogFileServlet.java @@ -0,0 +1,105 @@ +/* + * 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.qpid.server.management.plugin.servlet; + +import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.LogManager; +import org.apache.qpid.server.management.plugin.log.LogFileDetails; +import org.apache.qpid.server.management.plugin.log.LogFileHelper; +import org.apache.qpid.server.management.plugin.servlet.rest.AbstractServlet; + +public class LogFileServlet extends AbstractServlet +{ + private static final long serialVersionUID = 1L; + + public static final String LOGS_FILE_NAME = "qpid-logs-%s.zip"; + public static final String DATE_FORMAT = "yyyy-MM-dd-mmHHss"; + + @SuppressWarnings("unchecked") + private LogFileHelper _helper = new LogFileHelper(Collections.list(LogManager.getRootLogger().getAllAppenders())); + + @Override + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, + ServletException + { + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + + if (!getBroker().getSecurityManager().authoriseLogsAccess()) + { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Log files download is denied"); + return; + } + + String[] requestedFiles = request.getParameterValues("l"); + + if (requestedFiles == null || requestedFiles.length == 0) + { + response.setStatus(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + List<LogFileDetails> logFiles = null; + + try + { + logFiles = _helper.findLogFileDetails(requestedFiles); + } + catch(IllegalArgumentException e) + { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + + if (logFiles.size() == 0) + { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } + + String fileName = String.format(LOGS_FILE_NAME, new SimpleDateFormat(DATE_FORMAT).format(new Date())); + response.setStatus(HttpServletResponse.SC_OK); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName); + response.setContentType(LogFileHelper.ZIP_MIME_TYPE); + + OutputStream os = response.getOutputStream(); + try + { + _helper.writeLogFiles(logFiles, os); + } + finally + { + if (os != null) + { + os.close(); + } + } + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java index 9614ded3d8..c0f4b55f64 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.management.plugin.servlet.rest; import java.io.IOException; +import java.io.PrintWriter; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; @@ -40,6 +41,10 @@ import org.apache.qpid.server.management.plugin.HttpManagementConfiguration; import org.apache.qpid.server.management.plugin.HttpManagementUtil; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.SecurityManager; +import org.codehaus.jackson.JsonGenerationException; +import org.codehaus.jackson.map.JsonMappingException; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; public abstract class AbstractServlet extends HttpServlet { @@ -219,14 +224,7 @@ public abstract class AbstractServlet extends HttpServlet } finally { - try - { - SecurityManager.setThreadSubject(null); - } - finally - { - AMQShortString.clearLocalCache(); - } + SecurityManager.setThreadSubject(null); } } @@ -261,4 +259,20 @@ public abstract class AbstractServlet extends HttpServlet throw new RuntimeException("Failed to send error response code " + errorCode, e); } } + + protected void sendJsonResponse(Object object, HttpServletResponse response) throws IOException, + JsonGenerationException, JsonMappingException + { + response.setStatus(HttpServletResponse.SC_OK); + + response.setHeader("Cache-Control","no-cache"); + response.setHeader("Pragma","no-cache"); + response.setDateHeader ("Expires", 0); + response.setContentType("application/json"); + + final PrintWriter writer = response.getWriter(); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); + mapper.writeValue(writer, object); + } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java index 75e5bd9842..9ba36bb5c2 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/HelperServlet.java @@ -41,6 +41,8 @@ import org.codehaus.jackson.map.SerializationConfig; public class HelperServlet extends AbstractServlet { + private static final long serialVersionUID = 1L; + private static final String PARAM_ACTION = "action"; private Map<String, Action> _actions; @@ -55,6 +57,8 @@ public class HelperServlet extends AbstractServlet new ListAuthenticationProviderAttributes(), new ListBrokerAttribute(Broker.SUPPORTED_VIRTUALHOST_STORE_TYPES, "ListMessageStoreTypes"), new ListBrokerAttribute(Broker.SUPPORTED_VIRTUALHOST_TYPES, "ListVirtualHostTypes"), + new ListBrokerAttribute(Broker.SUPPORTED_PREFERENCES_PROVIDERS_TYPES, "ListPreferencesProvidersTypes"), + new ListBrokerAttribute(Broker.PRODUCT_VERSION, "version"), new ListGroupProviderAttributes(), new ListAccessControlProviderAttributes(), new PluginClassProviderAction() diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogFileListingServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogFileListingServlet.java new file mode 100644 index 0000000000..b6face18e3 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogFileListingServlet.java @@ -0,0 +1,68 @@ +/* + * 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.qpid.server.management.plugin.servlet.rest; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.LogManager; +import org.apache.qpid.server.management.plugin.log.LogFileDetails; +import org.apache.qpid.server.management.plugin.log.LogFileHelper; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.SerializationConfig; + +public class LogFileListingServlet extends AbstractServlet +{ + private static final long serialVersionUID = 1L; + + @SuppressWarnings("unchecked") + private LogFileHelper _helper = new LogFileHelper(Collections.list(LogManager.getRootLogger().getAllAppenders())); + + @Override + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, + ServletException + { + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + + if (!getBroker().getSecurityManager().authoriseLogsAccess()) + { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Log files download is denied"); + return; + } + + List<LogFileDetails> logFiles = _helper.getLogFileDetails(false); + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + + final PrintWriter writer = response.getWriter(); + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true); + mapper.writeValue(writer, logFiles); + + response.setStatus(HttpServletResponse.SC_OK); + } + +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java index f2cf5d7734..35523ddf0f 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/LogRecordsServlet.java @@ -31,6 +31,10 @@ import org.codehaus.jackson.map.SerializationConfig; public class LogRecordsServlet extends AbstractServlet { + private static final long serialVersionUID = 2L; + + public static final String PARAM_LAST_LOG_ID = "lastLogId"; + public LogRecordsServlet() { super(); @@ -46,12 +50,31 @@ public class LogRecordsServlet extends AbstractServlet response.setHeader("Pragma","no-cache"); response.setDateHeader ("Expires", 0); + if (!getBroker().getSecurityManager().authoriseLogsAccess()) + { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Broker logs access is denied"); + return; + } + + long lastLogId = 0; + try + { + lastLogId = Long.parseLong(request.getParameter(PARAM_LAST_LOG_ID)); + } + catch(Exception e) + { + // ignore null and incorrect parameter values + } + List<Map<String,Object>> logRecords = new ArrayList<Map<String, Object>>(); LogRecorder logRecorder = getBroker().getLogRecorder(); for(LogRecorder.Record record : logRecorder) { - logRecords.add(logRecordToObject(record)); + if (record.getId() > lastLogId) + { + logRecords.add(logRecordToObject(record)); + } } final PrintWriter writer = response.getWriter(); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java index 49e0c2b1bf..83208516c7 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java @@ -80,6 +80,7 @@ public class MessageServlet extends AbstractServlet response.setHeader("Cache-Control","no-cache"); response.setHeader("Pragma","no-cache"); response.setDateHeader ("Expires", 0); + response.setContentType("application/json"); final PrintWriter writer = response.getWriter(); ObjectMapper mapper = new ObjectMapper(); @@ -352,20 +353,32 @@ public class MessageServlet extends AbstractServlet if(messageHeader != null) { addIfPresent(object, "messageId", messageHeader.getMessageId()); - addIfPresent(object, "expirationTime", messageHeader.getExpiration()); + addIfPresentAndNotZero(object, "expirationTime", messageHeader.getExpiration()); addIfPresent(object, "applicationId", messageHeader.getAppId()); addIfPresent(object, "correlationId", messageHeader.getCorrelationId()); addIfPresent(object, "encoding", messageHeader.getEncoding()); addIfPresent(object, "mimeType", messageHeader.getMimeType()); addIfPresent(object, "priority", messageHeader.getPriority()); addIfPresent(object, "replyTo", messageHeader.getReplyTo()); - addIfPresent(object, "timestamp", messageHeader.getTimestamp()); + addIfPresentAndNotZero(object, "timestamp", messageHeader.getTimestamp()); addIfPresent(object, "type", messageHeader.getType()); addIfPresent(object, "userId", messageHeader.getUserId()); } } + private void addIfPresentAndNotZero(Map<String, Object> object, String name, Object property) + { + if(property instanceof Number) + { + Number value = (Number)property; + if (value.longValue() != 0) + { + object.put(name, property); + } + } + } + private void addIfPresent(Map<String, Object> object, String name, Object property) { if(property != null) diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/PreferencesServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/PreferencesServlet.java new file mode 100644 index 0000000000..bf2a88a2c1 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/PreferencesServlet.java @@ -0,0 +1,137 @@ +/* + * + * 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.qpid.server.management.plugin.servlet.rest; + +import java.io.IOException; +import java.net.SocketAddress; +import java.security.Principal; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.security.auth.Subject; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.qpid.server.management.plugin.HttpManagementUtil; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; +import org.codehaus.jackson.map.ObjectMapper; + +public class PreferencesServlet extends AbstractServlet +{ + private static final long serialVersionUID = 1L; + + @Override + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, + ServletException + { + PreferencesProvider preferencesProvider = getPreferencesProvider(request); + if (preferencesProvider == null) + { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "Preferences provider is not configured"); + return; + } + String userName = getAuthenticatedUserName(request); + Map<String, Object> preferences = preferencesProvider.getPreferences(userName); + if (preferences == null) + { + preferences = Collections.<String, Object>emptyMap(); + } + sendJsonResponse(preferences, response); + } + + /* + * replace preferences + */ + @Override + protected void doPutWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + PreferencesProvider preferencesProvider = getPreferencesProvider(request); + if (preferencesProvider == null) + { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "Preferences provider is not configured"); + return; + } + String userName = getAuthenticatedUserName(request); + + ObjectMapper mapper = new ObjectMapper(); + + @SuppressWarnings("unchecked") + Map<String, Object> newPreferences = mapper.readValue(request.getInputStream(), LinkedHashMap.class); + + preferencesProvider.deletePreferences(userName); + Map<String, Object> preferences = preferencesProvider.setPreferences(userName, newPreferences); + if (preferences == null) + { + preferences = Collections.<String, Object>emptyMap(); + } + sendJsonResponse(preferences, response); + } + + /* + * update preferences + */ + @Override + protected void doPostWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException + { + PreferencesProvider preferencesProvider = getPreferencesProvider(request); + if (preferencesProvider == null) + { + throw new IllegalStateException("Preferences provider is not configured"); + } + String userName = getAuthenticatedUserName(request); + + ObjectMapper mapper = new ObjectMapper(); + + @SuppressWarnings("unchecked") + Map<String, Object> newPreferences = mapper.readValue(request.getInputStream(), LinkedHashMap.class); + Map<String, Object> preferences = preferencesProvider.setPreferences(userName, newPreferences); + if (preferences == null) + { + preferences = Collections.<String, Object>emptyMap(); + } + sendJsonResponse(preferences, response); + } + + private String getAuthenticatedUserName(HttpServletRequest request) + { + Subject subject = getAuthorisedSubject(request); + Principal principal = AuthenticatedPrincipal.getAuthenticatedPrincipalFromSubject(subject); + return principal.getName(); + } + + private PreferencesProvider getPreferencesProvider(HttpServletRequest request) + { + SocketAddress localAddress = HttpManagementUtil.getSocketAddress(request); + AuthenticationProvider authenticationProvider = getManagementConfiguration().getAuthenticationProvider(localAddress); + if (authenticationProvider == null) + { + throw new IllegalStateException("Authentication provider is not found"); + } + return authenticationProvider.getPreferencesProvider(); + } +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java index 1cebe3ec19..8ea0aa538a 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/RestServlet.java @@ -123,7 +123,7 @@ public class RestServlet extends AbstractServlet if(names.size() > _hierarchy.length) { - throw new IllegalArgumentException("Too many entries in path"); + throw new IllegalArgumentException("Too many entries in path. Expected " + _hierarchy.length + "; path: " + names); } } @@ -337,7 +337,7 @@ public class RestServlet extends AbstractServlet if(names.size() != _hierarchy.length) { throw new IllegalArgumentException("Path to object to create must be fully specified. " - + "Found " + names.size() + " expecting " + _hierarchy.length); + + "Found " + names + " of size " + names.size() + " expecting " + _hierarchy.length); } } diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java index b67c83dc7a..2b035fed8f 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/SaslServlet.java @@ -278,7 +278,7 @@ public class SaslServlet extends AbstractServlet private SubjectCreator getSubjectCreator(HttpServletRequest request) { SocketAddress localAddress = HttpManagementUtil.getSocketAddress(request); - return HttpManagementUtil.getManagementConfiguration(getServletContext()).getSubjectCreator(localAddress); + return HttpManagementUtil.getManagementConfiguration(getServletContext()).getAuthenticationProvider(localAddress).getSubjectCreator(); } @Override diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java new file mode 100644 index 0000000000..808e3210dd --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/UserPreferencesServlet.java @@ -0,0 +1,215 @@ +/* + * + * 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.qpid.server.management.plugin.servlet.rest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.model.AuthenticationProvider; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.PreferencesProvider; +import org.apache.qpid.server.model.User; +import org.apache.qpid.server.security.access.Operation; + +public class UserPreferencesServlet extends AbstractServlet +{ + private static final Logger LOGGER = Logger.getLogger(UserPreferencesServlet.class); + private static final long serialVersionUID = 1L; + + @Override + protected void doGetWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException, + ServletException + { + String[] pathElements = null; + if (request.getPathInfo() != null && request.getPathInfo().length() > 0) + { + pathElements = request.getPathInfo().substring(1).split("/"); + } + if (pathElements != null && pathElements.length > 1) + { + getUserPreferences(pathElements[0], pathElements[1], response); + } + else + { + getUserList(pathElements, response); + } + } + + private void getUserPreferences(String authenticationProviderName, String userId, HttpServletResponse response) + throws IOException + { + if (!userPreferencesOperationAuthorized(userId)) + { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Vieweing of preferences is not allowed"); + return; + } + Map<String, Object> preferences = null; + PreferencesProvider preferencesProvider = getPreferencesProvider(authenticationProviderName); + if (preferencesProvider == null) + { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "Preferences provider is not configured"); + return; + } + preferences = preferencesProvider.getPreferences(userId); + + sendJsonResponse(preferences, response); + } + + private void getUserList(String[] pathElements, HttpServletResponse response) throws IOException + { + List<Map<String, Object>> users = null; + try + { + users = getUsers(pathElements); + } + catch (Exception e) + { + LOGGER.debug("Bad preferences request", e); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); + } + sendJsonResponse(users, response); + } + + private PreferencesProvider getPreferencesProvider(String authenticationProviderName) + { + AuthenticationProvider authenticationProvider = getAuthenticationProvider(authenticationProviderName); + if (authenticationProvider == null) + { + throw new IllegalArgumentException(String.format("Authentication provider '%s' is not found", + authenticationProviderName)); + } + PreferencesProvider preferencesProvider = authenticationProvider.getPreferencesProvider(); + return preferencesProvider; + } + + private AuthenticationProvider getAuthenticationProvider(String authenticationProviderName) + { + Broker broker = getBroker(); + Collection<AuthenticationProvider> authenticationProviders = broker.getAuthenticationProviders(); + for (AuthenticationProvider authenticationProvider : authenticationProviders) + { + if (authenticationProviderName.equals(authenticationProvider.getName())) + { + return authenticationProvider; + } + } + return null; + } + + private List<Map<String, Object>> getUsers(String[] pathElements) + { + List<Map<String, Object>> users = new ArrayList<Map<String, Object>>(); + String authenticationProviderName = pathElements != null && pathElements.length > 0 ? pathElements[0] : null; + + Broker broker = getBroker(); + Collection<AuthenticationProvider> authenticationProviders = broker.getAuthenticationProviders(); + for (AuthenticationProvider authenticationProvider : authenticationProviders) + { + if (authenticationProviderName != null && !authenticationProvider.getName().equals(authenticationProviderName)) + { + continue; + } + PreferencesProvider preferencesProvider = authenticationProvider.getPreferencesProvider(); + if (preferencesProvider != null) + { + Set<String> usernames = preferencesProvider.listUserIDs(); + for (String name : usernames) + { + Map<String, Object> userMap = new HashMap<String, Object>(); + userMap.put(User.NAME, name); + userMap.put("authenticationProvider", authenticationProvider.getName()); + users.add(userMap); + } + } + } + return users; + } + + /* + * removes preferences + */ + @Override + protected void doDeleteWithSubjectAndActor(HttpServletRequest request, HttpServletResponse response) throws IOException + { + final List<String[]> userData = new ArrayList<String[]>(); + for (String name : request.getParameterValues("user")) + { + String[] elements = name.split("/"); + if (elements.length != 2) + { + throw new IllegalArgumentException("Illegal parameter"); + } + userData.add(elements); + } + + if (!userData.isEmpty()) + { + Broker broker = getBroker(); + Collection<AuthenticationProvider> authenticationProviders = broker.getAuthenticationProviders(); + for (Iterator<String[]> it = userData.iterator(); it.hasNext();) + { + String[] data = (String[]) it.next(); + String authenticationProviderName = data[0]; + String userId = data[1]; + + for (AuthenticationProvider authenticationProvider : authenticationProviders) + { + if (authenticationProviderName.equals(authenticationProvider.getName())) + { + PreferencesProvider preferencesProvider = authenticationProvider.getPreferencesProvider(); + if (preferencesProvider != null) + { + Set<String> usernames = preferencesProvider.listUserIDs(); + if (usernames.contains(userId)) + { + if (!userPreferencesOperationAuthorized(userId)) + { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Deletion of preferences is not allowed"); + return; + } + preferencesProvider.deletePreferences(userId); + } + } + break; + } + } + } + } + + } + + private boolean userPreferencesOperationAuthorized(String userId) + { + return getBroker().getSecurityManager().authoriseUserOperation(Operation.UPDATE, userId); + } +} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/addPreferencesProvider.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/addPreferencesProvider.html new file mode 100644 index 0000000000..ac5dd32119 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/addPreferencesProvider.html @@ -0,0 +1,47 @@ +<!-- + ~ 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. + --> +<div class="dijitHidden"> + <div data-dojo-type="dijit.Dialog" data-dojo-props="title:'Preferences Provider'" id="addPreferencesProvider"> + <form id="formAddPreferencesProvider" method="post" data-dojo-type="dijit.form.Form"> + <input type="hidden" id="preferencesProvider.id" name="id"/> + <div style="height:100px; width:420px; overflow: auto"> + <table class="tableContainer-table tableContainer-table-horiz" width="100%" cellspacing="1"> + <tr> + <td class="tableContainer-labelCell" style="width: 200px;"><strong>Type*:</strong></td> + <td class="tableContainer-valueCell"><div id="addPreferencesProvider.selectPreferencesProviderDiv"></div></td> + </tr> + <tr> + <td class="tableContainer-labelCell" style="width: 200px;"><strong>Name*:</strong></td> + <td class="tableContainer-valueCell"><input type="text" name="name" + id="preferencesProvider.name" data-dojo-type="dijit.form.ValidationTextBox" + data-dojo-props="placeHolder: 'Name', + required: true, + missingMessage: 'A name must be supplied', + title: 'Enter name', + pattern: '^[\x20-\x2e\x30-\x7F]{1,255}$'" /></td> + </tr> + </table> + <div id="preferencesProvider.fieldsContainer"></div> + </div> + <div class="dijitDialogPaneActionBar"> + <!-- submit buttons --> + <input type="submit" value="Save Preferences Provider" data-dojo-props="label: 'Save Preferences Provider'" data-dojo-type="dijit.form.Button" /> + <input type="button" value="Cancel" data-dojo-props="label: 'Cancel'" data-dojo-type="dijit.form.Button" id="addPreferencesProvider.cancelButton"/> + </div> + </form> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/preferences/filesystempreferences/add.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/preferences/filesystempreferences/add.html new file mode 100644 index 0000000000..f46da4b017 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/preferences/filesystempreferences/add.html @@ -0,0 +1,29 @@ +<!-- + ~ 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. + --> +<table class="tableContainer-table tableContainer-table-horiz" style="margin:0; width:100%" cellspacing="1"> + <tr> + <td class="tableContainer-labelCell" style="width: 200px;"><strong>Path*: </strong></td> + <td class="tableContainer-valueCell" > + <input type="text" name="path" + id="preferencesProvider.path" + data-dojo-type="dijit.form.ValidationTextBox" + data-dojo-props="placeHolder: 'Path/to/file', + required: true, + missingMessage: 'A path must be supplied', + title: 'Enter path'"/></td> + </tr> +</table> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/preferences/filesystempreferences/show.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/preferences/filesystempreferences/show.html new file mode 100644 index 0000000000..bc302d1e65 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/authenticationprovider/preferences/filesystempreferences/show.html @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<div style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 100px;">Path:</div> + <div class="fileSystemPreferencesProviderPath" style="float:left;"></div> +</div>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/common/TimeZoneSelector.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/common/TimeZoneSelector.html new file mode 100644 index 0000000000..7027a4555c --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/common/TimeZoneSelector.html @@ -0,0 +1,55 @@ +<!-- + - + - 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. + - + --> +<table cellpadding="0" cellspacing="2"> + <tr> + <td>Region</td> + <td> + <select class='timezoneRegion' name="region" data-dojo-type="dijit/form/FilteringSelect" data-dojo-props=" + placeholder: 'Select region', + required: true, + missingMessage: 'A region must be supplied', + title: 'Select region', + autoComplete: true, + value:'undefined'"> + <option value="undefined">Undefined</option> + <option value="Africa">Africa</option> + <option value="America">America</option> + <option value="Antarctica">Antarctica</option> + <option value="Arctic">Arctic</option> + <option value="Asia">Asia</option> + <option value="Atlantic">Atlantic</option> + <option value="Australia">Australia</option> + <option value="Europe">Europe</option> + <option value="Indian">Indian</option> + <option value="Pacific">Pacific</option> + </select> + </td> + <td>City</td> + <td> + <select class='timezoneCity' name="city" data-dojo-type="dijit/form/FilteringSelect" data-dojo-props=" + placeholder: 'Select city', + required: true, + missingMessage: 'A city must be supplied', + title: 'Select city'"> + </select> + </td> + </tr> +</table>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css index 4c8b79ab82..d9064f40c9 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/css/common.css @@ -92,4 +92,74 @@ div .messages { .formLabel-labelCell { font-weight: bold; +} + +.columnDefDialogButtonIcon { + background: url("../dojo/dojox/grid/enhanced/resources/images/sprite_icons.png") no-repeat; + background-position: -260px 2px; + width: 14px; + height: 14px; +} + +.logViewerIcon { + background: url("../images/log-viewer.png") no-repeat; + width: 14px; + height: 16px; +} + +.downloadLogsIcon { + background: url("../images/download.png") no-repeat; + width: 14px; + height: 14px; +} + +.dojoxGridFBarClearFilterButtontnIcon +{ + background: url("../dojo/dojox/grid/enhanced/resources/images/sprite_icons.png") no-repeat; + background-position: -120px -18px; + width: 14px; + height: 14px; +} + +.rowNumberLimitIcon +{ + background: url("../dojo/dojox/grid/enhanced/resources/images/sprite_icons.png") no-repeat; + background-position: -240px -18px; + width: 14px; + height: 14px; +} + +.gridRefreshIcon +{ + background: url("../images/refresh.png") no-repeat; + width: 16px; + height: 16px; +} + +.gridAutoRefreshIcon +{ + background: url("../images/auto-refresh.png") no-repeat; + width: 16px; + height: 16px; +} + +.redBackground tr{ background-color:#ffdcd7 !important; background-image: none !important;} +.yellowBackground tr{background-color:#fbfddf !important; background-image: none !important;} +.grayBackground tr{background-color:#eeeeee !important; background-image: none !important;} +.dojoxGridRowOdd.grayBackground tr{ background-color:#e9e9e9 !important; background-image: none !important;} +.dojoxGridRowOdd.yellowBackground tr{background-color:#fafdd5 !important; background-image: none !important;} +.dojoxGridRowOdd.redBackground tr{background-color:#f4c1c1 !important; background-image: none !important;} + +.preferencesIcon +{ + background: url("../images/gear.png") no-repeat; + width: 16px; + height: 16px; +} + +.helpIcon +{ + background: url("../images/help.png") no-repeat; + width: 16px; + height: 16px; }
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/grid/showColumnDefDialog.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/grid/showColumnDefDialog.html new file mode 100644 index 0000000000..5b6b8ad774 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/grid/showColumnDefDialog.html @@ -0,0 +1,32 @@ +<!-- + - + - 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. + - + --> +<div> + <div> + <div>Select columns to display:</div> + <div class="columnList"></div> + </div> + <div class="dijitDialogPaneActionBar"> + <button value="Display" data-dojo-type="dijit.form.Button" + class="displayButton" data-dojo-props="label: 'Display' "></button> + <button value="Cancel" data-dojo-type="dijit.form.Button" data-dojo-props="label: 'Cancel'" + class="cancelButton"></button> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/grid/showRowNumberLimitDialog.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/grid/showRowNumberLimitDialog.html new file mode 100644 index 0000000000..087d54c0f9 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/grid/showRowNumberLimitDialog.html @@ -0,0 +1,33 @@ +<!-- + - + - 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. + - + --> +<div> + <div> + <div>Set the maximum number of rows to cache and display:</div> + <input class="rowNumberLimit" data-dojo-type="dijit.form.NumberSpinner" + data-dojo-props="invalidMessage: 'Invalid value', required: true, smallDelta: 1,mconstraints: {min:1,max:65535,places:0, pattern: '#####'}, label: 'Maximum number of rows:', name: 'rowNumberLimit'"></input> + </div> + <div class="dijitDialogPaneActionBar"> + <button value="Submit" data-dojo-type="dijit.form.Button" + class="submitButton" data-dojo-props="label: 'Submit' "></button> + <button value="Cancel" data-dojo-type="dijit.form.Button" data-dojo-props="label: 'Cancel'" + class="cancelButton"></button> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/auto-refresh.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/auto-refresh.png Binary files differnew file mode 100644 index 0000000000..493636f467 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/auto-refresh.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/download.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/download.png Binary files differnew file mode 100644 index 0000000000..b64b41d476 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/download.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/gear.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/gear.png Binary files differnew file mode 100644 index 0000000000..0bb4394b46 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/gear.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/help.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/help.png Binary files differnew file mode 100644 index 0000000000..f7d3698d25 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/help.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/log-viewer.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/log-viewer.png Binary files differnew file mode 100644 index 0000000000..858fd48beb --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/log-viewer.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png Binary files differindex 95d49ea469..ae0fbb462f 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/qpid-logo.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/images/refresh.png b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/refresh.png Binary files differnew file mode 100644 index 0000000000..083044979b --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/images/refresh.png diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html index 4b97c464ec..4fc961ec12 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/index.html @@ -42,7 +42,6 @@ var dojoConfig = { tlmSiblingOfDojo:false, - parseOnLoad:true, async:true, baseUrl: getContextPath(), packages:[ @@ -58,7 +57,14 @@ </script> <script> - require(["dijit/layout/BorderContainer", + var qpidHelpLocation = "http://qpid.apache.org/releases/qpid-"; + var qpidHelpURL = null; + var qpidPreferences = null; + require([ + "dojo/_base/xhr", + "dojo/parser", + "qpid/management/Preferences", + "dijit/layout/BorderContainer", "dijit/layout/TabContainer", "dijit/layout/ContentPane", "dijit/TitlePane", @@ -66,7 +72,18 @@ "qpid/management/treeView", "qpid/management/controller", "qpid/common/footer", - "qpid/authorization/checkUser"]); + "qpid/authorization/checkUser"], function(xhr, parser, Preferences){ + parser.parse(); + qpidPreferences = new Preferences(); + xhr.get({ + sync: true, + url: "rest/helper?action=version", + handleAs: "json" + }).then(function(qpidVersion) { + qpidHelpURL = qpidHelpLocation + qpidVersion + "/java-broker/book/index.html"; + }); + + }); </script> </head> @@ -75,7 +92,27 @@ <div id="pageLayout" data-dojo-type="dijit.layout.BorderContainer" data-dojo-props="design: 'headline', gutters: false"> <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'top'"> <div id="header" class="header" style="float: left; width: 300px"></div> - <div id="login" style="float: right; display:none"><strong>User: </strong> <span id="authenticatedUser"></span><a href="logout">[logout]</a></div> + <div style="float: right;"> + <div id="login" style="display:none"><strong>User: </strong> <span id="authenticatedUser"></span><a href="logout">[logout]</a></div> + <div id="preferencesButton" style="float: right; margin-top: 0px;" data-dojo-type="dijit.form.DropDownButton" data-dojo-props="iconClass: 'preferencesIcon', title: 'Preferences', showLabel:false"> + <div data-dojo-type="dijit.Menu"> + <div data-dojo-type="dijit.MenuItem" data-dojo-props=" + iconClass: 'dijitIconFunction', + onClick: function(){ qpidPreferences.showDialog(); } "> + Preferences + </div> + <!-- + <div data-dojo-type="dijit.MenuItem" data-dojo-props="iconClass: 'dijitIconMail', onClick: function(){ console.log('TODO'); }"> + Contacts + </div> + --> + <div data-dojo-type="dijit.MenuItem" data-dojo-props="iconClass: 'helpIcon', onClick: function(){ + var newWindow = window.open(qpidHelpURL,'QpidHelp','height=600,width=600,scrollbars=1,location=1,resizable=1,status=0,toolbar=0,titlebar=1,menubar=0',true); newWindow.focus(); } "> + Help + </div> + </div> + </div> + </div> </div> <div data-dojo-type="dijit.layout.ContentPane" data-dojo-props="region:'leading', splitter: true" style="width:20%"> <div qpid-type="treeView" qpid-props="query: 'rest/structure'" ></div> @@ -104,4 +141,4 @@ </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js index 159c7458ed..d65e6c6e07 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/authorization/checkUser.js @@ -29,7 +29,7 @@ var updateUI = function updateUI(data) if(data.user) { dom.byId("authenticatedUser").innerHTML = entities.encode(String(data.user)); - dom.byId("login").style.display = "block"; + dom.byId("login").style.display = "inline"; } }; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/TimeZoneSelector.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/TimeZoneSelector.js new file mode 100644 index 0000000000..287fbc9619 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/TimeZoneSelector.js @@ -0,0 +1,176 @@ +/* + * + * 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. + * + */ +define([ + "dojo/_base/declare", + "dojo/_base/array", + "dojo/dom-construct", + "dojo/parser", + "dojo/query", + "dojo/store/Memory", + "dijit/_WidgetBase", + "dijit/registry", + "dojo/text!common/TimeZoneSelector.html", + "dijit/form/ComboBox", + "dijit/form/FilteringSelect", + "dojox/date/timezone", + "dojox/validate/us", + "dojox/validate/web", + "dojo/domReady!"], +function (declare, array, domConstruct, parser, query, Memory, _WidgetBase, registry, template) { + + var preferencesRegions = ["Africa","America","Antarctica","Arctic","Asia","Atlantic","Australia","Europe","Indian","Pacific"]; + + function initSupportedTimeZones() + { + var supportedTimeZones = []; + var allTimeZones = dojox.date.timezone.getAllZones(); + for(var i = 0; i < allTimeZones.length; i++) + { + var timeZone = allTimeZones[i]; + var elements = timeZone.split("/"); + if (elements.length > 1) + { + for(var j = 0; j<preferencesRegions.length; j++) + { + if (elements[0] == preferencesRegions[j]) + { + supportedTimeZones.push({id: timeZone, region: elements[0], city: elements.slice(1).join("/").replace("_", " ") }) + break; + } + } + } + } + return supportedTimeZones; + } + + function initSupportedRegions() + { + var supportedRegions = [{"id": "undefined", "name": "Undefined"}]; + for(var j = 0; j<preferencesRegions.length; j++) + { + supportedRegions.push({id: preferencesRegions[j], name: preferencesRegions[j] }); + } + return supportedRegions; + } + + return declare("qpid.common.TimeZoneSelector", [_WidgetBase], { + + value: null, + domNode: null, + _regionSelector: null, + _citySelector: null, + + constructor: function(args) + { + this._args = args; + }, + + buildRendering: function(){ + this.domNode = domConstruct.create("div", {innerHTML: template}); + parser.parse(this.domNode); + }, + + postCreate: function(){ + this.inherited(arguments); + + var supportedTimeZones = initSupportedTimeZones(); + + this._citySelector = registry.byNode(query(".timezoneCity", this.domNode)[0]); + this._citySelector.set("searchAttr", "city"); + this._citySelector.set("query", {region: /.*/}); + this._citySelector.set("labelAttr", "city"); + this._citySelector.set("store", new Memory({ data: supportedTimeZones })); + if (this._args.name) + { + this._citySelector.set("name", this._args.name); + } + this._regionSelector = registry.byNode(query(".timezoneRegion", this.domNode)[0]); + var supportedRegions = initSupportedRegions(); + this._regionSelector.set("store", new Memory({ data: supportedRegions })); + var self = this; + + this._regionSelector.on("change", function(value){ + if (value=="undefined") + { + self._citySelector.set("disabled", true); + self._citySelector.query.region = /.*/; + self.value = null; + self._citySelector.set("value", null); + } + else + { + self._citySelector.set("disabled", false); + self._citySelector.query.region = value || /.*/; + if (this.timeZone) + { + self._citySelector.set("value", this.timeZone); + this.timeZone = null; + } + else + { + self._citySelector.set("value", null); + } + } + }); + + this._citySelector.on("change", function(value){ + self.value = value; + }); + + this._setValueAttr(this._args.value); + }, + + _setValueAttr: function(value) + { + if (value) + { + var elements = value.split("/"); + if (elements.length > 1) + { + this._regionSelector.timeZone = value; + this._regionSelector.set("value", elements[0]); + this._citySelector.set("value", value); + } + else + { + this._regionSelector.set("value", "undefined"); + } + } + else + { + this._regionSelector.set("value", "undefined"); + } + this.value = value; + }, + + destroy: function() + { + if (this.domNode) + { + this.domNode.destroy(); + this.domNode = null; + } + _regionSelector: null; + _citySelector: null; + } + + }); +});
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js index f7ede1a7f7..ea3ba78372 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/UpdatableStore.js @@ -23,12 +23,13 @@ define(["dojo/store/Memory", "dojo/data/ObjectStore", "dojo/store/Observable"], function (Memory, DataGrid, ObjectStore, Observable) { - function UpdatableStore( data, divName, structure, func, props, Grid ) { + function UpdatableStore( data, divName, structure, func, props, Grid, notObservable ) { var that = this; var GridType = DataGrid; - that.store = Observable(Memory({data: data, idProperty: "id"})); + that.memoryStore = new Memory({data: data, idProperty: "id"}); + that.store = notObservable? that.memoryStore : new Observable(that.memoryStore); that.dataStore = ObjectStore({objectStore: that.store}); var gridProperties = { store: that.dataStore, @@ -63,7 +64,7 @@ define(["dojo/store/Memory", UpdatableStore.prototype.update = function(data) { - + var changed = false; var store = this.store; var theItem; @@ -78,7 +79,7 @@ define(["dojo/store/Memory", } } store.remove(object.id); - + changed = true; }); // iterate over data... @@ -91,20 +92,84 @@ define(["dojo/store/Memory", if(theItem[ propName ] != data[i][ propName ]) { theItem[ propName ] = data[i][ propName ]; modified = true; + changed = true; } } } if(modified) { // ... check attributes for updates store.notify(theItem, data[i].id); + changed = true; } } else { // ,,, if not in the store then add store.put(data[i]); + changed = true; } } } + return changed; + }; + + function removeItemsFromArray(items, numberToRemove) + { + if (items) + { + if (numberToRemove > 0 && items.length > 0) + { + if (numberToRemove >= items.length) + { + numberToRemove = numberToRemove - items.length; + items.length = 0 + } + else + { + items.splice(0, numberToRemove); + numberToRemove = 0; + } + } + } + return numberToRemove; + }; + + UpdatableStore.prototype.append = function(data, limit) + { + var changed = false; + var items = this.memoryStore.data; + + if (limit) + { + var totalSize = items.length + (data ? data.length : 0); + var numberToRemove = totalSize - limit; + + if (numberToRemove > 0) + { + changed = true; + numberToRemove = removeItemsFromArray(items, numberToRemove); + if (numberToRemove > 0) + { + removeItemsFromArray(data, numberToRemove); + } + } + } + + if (data && data.length > 0) + { + changed = true; + items.push.apply(items, data); + } + + this.memoryStore.setData(items); + return changed; + }; + + UpdatableStore.prototype.close = function() + { + this.dataStore.close(); + this.dataStore = null; + this.store = null; + this.memoryStore = null; }; return UpdatableStore; }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/ColumnDefDialog.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/ColumnDefDialog.js new file mode 100644 index 0000000000..d285dfaad6 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/ColumnDefDialog.js @@ -0,0 +1,140 @@ +/* + * + * 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. + * + */ + +define([ + "dojo/_base/declare", + "dojo/_base/event", + "dojo/_base/array", + "dojo/_base/lang", + "dojo/parser", + "dojo/dom-construct", + "dojo/query", + "dijit/registry", + "dijit/form/Button", + "dijit/form/CheckBox", + "dojox/grid/enhanced/plugins/Dialog", + "dojo/text!../../../grid/showColumnDefDialog.html", + "dojo/domReady!" +], function(declare, event, array, lang, parser, dom, query, registry, Button, CheckBox, Dialog, template ){ + + +return declare("qpid.common.grid.ColumnDefDialog", null, { + + grid: null, + containerNode: null, + _columns: [], + _dialog: null, + + constructor: function(args){ + var grid = this.grid = args.grid; + + this.containerNode = dom.create("div", {innerHTML: template}); + parser.parse(this.containerNode); + + var submitButton = registry.byNode(query(".displayButton", this.containerNode)[0]); + this.closeButton = registry.byNode(query(".cancelButton", this.containerNode)[0]); + var columnsContainer = query(".columnList", this.containerNode)[0]; + + this._buildColumnWidgets(columnsContainer); + + this._dialog = new Dialog({ + "refNode": this.grid.domNode, + "title": "Grid Columns", + "content": this.containerNode + }); + + var self = this; + submitButton.on("click", function(e){self._onColumnsSelect(e); }); + this.closeButton.on("click", function(e){self._dialog.hide(); }); + + this._dialog.startup(); + }, + + destroy: function(){ + this._dialog.destroyRecursive(); + this._dialog = null; + this.grid = null; + this.containerNode = null; + this._columns = null; + }, + + showDialog: function(){ + this._initColumnWidgets(); + this._dialog.show(); + }, + + _initColumnWidgets: function() + { + var cells = this.grid.layout.cells; + for(var i in cells) + { + var cell = cells[i]; + this._columns[cell.name].checked = !cell.hidden; + } + }, + + _onColumnsSelect: function(evt){ + event.stop(evt); + var grid = this.grid; + grid.beginUpdate(); + var cells = grid.layout.cells; + try + { + for(var i in cells) + { + var cell = cells[i]; + var widget = this._columns[cell.name]; + grid.layout.setColumnVisibility(i, widget.checked); + } + } + finally + { + grid.endUpdate(); + this._dialog.hide(); + } + }, + + _buildColumnWidgets: function(columnsContainer) + { + var cells = this.grid.layout.cells; + for(var i in cells) + { + var cell = cells[i]; + var widget = new dijit.form.CheckBox({ + required: false, + checked: !cell.hidden, + label: cell.name, + name: this.grid.id + "_cchb_ " + i + }); + + this._columns[cell.name] = widget; + + var div = dom.create("div"); + div.appendChild(widget.domNode); + div.appendChild(dom.create("span", {innerHTML: cell.name})); + + columnsContainer.appendChild(div); + } + } + + }); + +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/EnhancedFilter.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/EnhancedFilter.js new file mode 100644 index 0000000000..9c0baf3111 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/EnhancedFilter.js @@ -0,0 +1,229 @@ +/* + * + * 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. + * + */ + +define([ + "dojo/_base/declare", + "dojo/_base/lang", + "dojo/_base/array", + "dijit/Toolbar", + "dojox/grid/enhanced/_Plugin", + "dojox/grid/enhanced/plugins/Dialog", + "dojox/grid/enhanced/plugins/filter/FilterLayer", + "dojox/grid/enhanced/plugins/filter/FilterDefDialog", + "dojox/grid/enhanced/plugins/filter/FilterStatusTip", + "dojox/grid/enhanced/plugins/filter/ClearFilterConfirm", + "dojox/grid/EnhancedGrid", + "dojo/i18n!dojox/grid/enhanced/nls/Filter", + "qpid/common/grid/EnhancedFilterTools" +], function(declare, lang, array, Toolbar, _Plugin, + Dialog, FilterLayer, FilterDefDialog, FilterStatusTip, ClearFilterConfirm, EnhancedGrid, nls, EnhancedFilterTools){ + + // override CriteriaBox#_getColumnOptions to show criteria for hidden columns with EnhancedFilter + dojo.extend(dojox.grid.enhanced.plugins.filter.CriteriaBox, { + _getColumnOptions: function(){ + var colIdx = this.dlg.curColIdx >= 0 ? String(this.dlg.curColIdx) : "anycolumn"; + var filterHidden = this.plugin.filterHidden; + return array.map(array.filter(this.plugin.grid.layout.cells, function(cell){ + return !(cell.filterable === false || (!filterHidden && cell.hidden)); + }), function(cell){ + return { + label: cell.name || cell.field, + value: String(cell.index), + selected: colIdx == String(cell.index) + }; + }); + } + }); + + // Enhanced filter has extra functionality for refreshing, limiting rows, displaying/hiding columns in the grid + var EnhancedFilter = declare("qpid.common.grid.EnhancedFilter", _Plugin, { + // summary: + // Accept the same plugin parameters as dojox.grid.enhanced.plugins.Filter and the following: + // + // filterHidden: boolean: + // Whether to display filtering criteria for hidden columns. Default to true. + // + // defaulGridRowLimit: int: + // Default limit for numbers of items to cache in the gris dtore + // + // disableFiltering: boolean: + // Whether to disable a filtering including filter button, clear filter button and filter summary. + // + // toolbar: dijit.Toolbar: + // An instance of toolbar to add the enhanced filter widgets. + + + // name: String + // plugin name + name: "enhancedFilter", + + // filterHidden: Boolean + // whether to filter hidden columns + filterHidden: true, + + constructor: function(grid, args){ + // summary: + // See constructor of dojox.grid.enhanced._Plugin. + this.grid = grid; + this.nls = nls; + + args = this.args = lang.isObject(args) ? args : {}; + if(typeof args.ruleCount != 'number' || args.ruleCount < 0){ + args.ruleCount = 0; + } + var rc = this.ruleCountToConfirmClearFilter = args.ruleCountToConfirmClearFilter; + if(rc === undefined){ + this.ruleCountToConfirmClearFilter = 5; + } + + if (args.filterHidden){ + this.filterHidden = args.filterHidden; + } + this.defaulGridRowLimit = args.defaulGridRowLimit; + this.disableFiltering = args.disableFiltering; + + //Install UI components + var obj = { "plugin": this }; + + this.filterBar = ( args.toolbar && args.toolbar instanceof dijit.Toolbar) ? args.toolbar: new Toolbar(); + + if (!this.disableFiltering) + { + //Install filter layer + this._wrapStore(); + + this.clearFilterDialog = new Dialog({ + refNode: this.grid.domNode, + title: this.nls["clearFilterDialogTitle"], + content: new ClearFilterConfirm(obj) + }); + + this.filterDefDialog = new FilterDefDialog(obj); + + nls["statusTipTitleNoFilter"] = "Filter is not set"; + nls["statusTipMsg"] = "Click on 'Set Filter' button to specify filtering conditions"; + this.filterStatusTip = new FilterStatusTip(obj); + + var self = this; + var toggleClearFilterBtn = function (arg){ self.enhancedFilterTools.toggleClearFilterBtn(arg); }; + + this.filterBar.toggleClearFilterBtn = toggleClearFilterBtn; + + this.grid.isFilterBarShown = function (){return true}; + + this.connect(this.grid.layer("filter"), "onFilterDefined", function(filter){ + toggleClearFilterBtn(true); + }); + + //Expose the layer event to grid. + grid.onFilterDefined = function(){}; + this.connect(grid.layer("filter"), "onFilterDefined", function(filter){ + grid.onFilterDefined(grid.getFilter(), grid.getFilterRelation()); + }); + } + + // add extra buttons into toolbar + this.enhancedFilterTools = new EnhancedFilterTools({ + grid: grid, + toolbar: this.filterBar, + filterStatusTip: this.filterStatusTip, + clearFilterDialog: this.clearFilterDialog, + filterDefDialog: this.filterDefDialog, + defaulGridRowLimit: this.defaulGridRowLimit, + disableFiltering: this.disableFiltering, + nls: nls + }); + + this.filterBar.placeAt(this.grid.viewsHeaderNode, "before"); + this.filterBar.startup(); + + }, + + destroy: function(){ + this.inherited(arguments); + try + { + if (this.filterDefDialog) + { + this.filterDefDialog.destroy(); + this.filterDefDialog = null; + } + if (this.grid) + { + this.grid.unwrap("filter"); + this.grid = null; + } + if (this.filterBar) + { + this.filterBar.destroyRecursive(); + this.filterBar = null; + } + if (this.enhancedFilterTools) + { + this.enhancedFilterTools.destroy(); + this.enhancedFilterTools = null; + } + if (this.clearFilterDialog) + { + this.clearFilterDialog.destroyRecursive(); + this.clearFilterDialog = null; + } + if (this.filterStatusTip) + { + this.filterStatusTip.destroy(); + this.filterStatusTip = null; + } + this.args = null; + + }catch(e){ + console.warn("Filter.destroy() error:",e); + } + }, + + _wrapStore: function(){ + var g = this.grid; + var args = this.args; + var filterLayer = args.isServerSide ? new FilterLayer.ServerSideFilterLayer(args) : + new FilterLayer.ClientSideFilterLayer({ + cacheSize: args.filterCacheSize, + fetchAll: args.fetchAllOnFirstFilter, + getter: this._clientFilterGetter + }); + FilterLayer.wrap(g, "_storeLayerFetch", filterLayer); + + this.connect(g, "_onDelete", lang.hitch(filterLayer, "invalidate")); + }, + + onSetStore: function(store){ + this.filterDefDialog.clearFilter(true); + }, + + _clientFilterGetter: function(/* data item */ datarow,/* cell */cell, /* int */rowIndex){ + return cell.get(rowIndex, datarow); + } + + }); + + EnhancedGrid.registerPlugin(EnhancedFilter); + + return EnhancedFilter; + +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/EnhancedFilterTools.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/EnhancedFilterTools.js new file mode 100644 index 0000000000..b1645b4905 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/EnhancedFilterTools.js @@ -0,0 +1,270 @@ +/* + * + * 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. + * + */ + +define([ + "dojo/_base/declare", + "dojo/_base/event", + "dijit/form/Button", + "dijit/form/ToggleButton", + "qpid/common/grid/RowNumberLimitDialog", + "qpid/common/grid/ColumnDefDialog", + "qpid/common/grid/FilterSummary" +], function(declare, event, Button, ToggleButton, RowNumberLimitDialog, ColumnDefDialog, FilterSummary){ + + var _stopEvent = function (evt){ + try{ + if(evt && evt.preventDefault){ + event.stop(evt); + } + }catch(e){} + }; + + return declare("qpid.common.grid.EnhancedFilterTools", null, { + + grid: null, + filterBar: null, + filterStatusTip: null, + clearFilterDialog: null, + filterDefDialog: null, + + columnDefDialog: null, + columnDefButton: null, + filterDefButton: null, + clearFilterButton: null, + filterSummary: null, + setRowNumberLimitButton: null, + setRowNumberLimitDialog: null, + refreshButton: null, + autoRefreshButton: null, + + constructor: function(params) + { + this.inherited(arguments); + + this.filterBar = params.toolbar; + this.grid = params.grid; + this.filterStatusTip= params.filterStatusTip; + this.clearFilterDialog = params.clearFilterDialog; + this.filterDefDialog = params.filterDefDialog; + + this._addRefreshButtons(); + this._addRowLimitButton(params.defaulGridRowLimit); + this._addColumnsButton(); + + if (!params.disableFiltering) + { + this._addFilteringTools(params.nls); + } + }, + + toggleClearFilterBtn: function(clearFlag) + { + var filterLayer = this.grid.layer("filter"); + var filterSet = filterLayer && filterLayer.filterDef && filterLayer.filterDef(); + this.clearFilterButton.set("disabled", !filterSet); + }, + + destroy: function() + { + this.inherited(arguments); + + if (this.columnDefDialog) + { + this.columnDefDialog.destroy(); + this.columnDefDialog = null; + } + if (this.columnDefButton) + { + this.columnDefButton.destroy(); + this.columnDefButton = null; + } + if (this.filterDefButton) + { + this.filterDefButton.destroy(); + this.filterDefButton = null; + } + if (this.clearFilterButton) + { + this.clearFilterButton.destroy(); + this.clearFilterButton = null; + } + if (this.filterSummary) + { + this.filterSummary.destroy(); + this.filterSummary = null; + } + if (this.setRowNumberLimitButton) + { + this.setRowNumberLimitButton.destroy(); + this.setRowNumberLimitButton = null; + } + if (this.setRowNumberLimitDialog) + { + this.setRowNumberLimitDialog.destroy(); + this.setRowNumberLimitDialog = null; + } + if (this.refreshButton) + { + this.refreshButton.destroy(); + this.refreshButton = null; + } + if (this.autoRefreshButton) + { + this.autoRefreshButton.destroy(); + this.autoRefreshButton = null; + } + + this.grid = null; + this.filterBar = null; + this.filterStatusTip = null; + this.clearFilterDialog = null; + this.filterDefDialog = null; + }, + + _addRefreshButtons: function() + { + var self = this; + this.refreshButton = new dijit.form.Button({ + label: "Refresh", + type: "button", + iconClass: "gridRefreshIcon", + title: "Manual Refresh" + }); + + this.autoRefreshButton = new dijit.form.ToggleButton({ + label: "Auto Refresh", + type: "button", + iconClass: "gridAutoRefreshIcon", + title: "Auto Refresh" + }); + + this.autoRefreshButton.on("change", function(value){ + self.grid.updater.updatable=value; + self.refreshButton.set("disabled", value); + }); + + this.refreshButton.on("click", function(value){ + self.grid.updater.performUpdate(); + }); + + this.filterBar.addChild(this.autoRefreshButton); + this.filterBar.addChild(this.refreshButton); + }, + + _addRowLimitButton: function(defaulGridRowLimit) + { + var self = this; + this.setRowNumberLimitButton = new dijit.form.Button({ + label: "Set Row Limit", + type: "button", + iconClass: "rowNumberLimitIcon", + title: "Set Row Number Limit" + }); + this.setRowNumberLimitButton.set("title", "Set Row Number Limit (Current: " + defaulGridRowLimit +")"); + + this.setRowNumberLimitDialog = new RowNumberLimitDialog(this.grid.domNode, function(newLimit){ + if (newLimit > 0 && self.grid.updater.appendLimit != newLimit ) + { + self.grid.updater.appendLimit = newLimit; + self.grid.updater.performRefresh([]); + self.setRowNumberLimitButton.set("title", "Set Row Number Limit (Current: " + newLimit +")"); + } + }); + + this.setRowNumberLimitButton.on("click", function(evt){ + self.setRowNumberLimitDialog.showDialog(self.grid.updater.appendLimit); + }); + + this.filterBar.addChild(this.setRowNumberLimitButton); + }, + + _addColumnsButton: function() + { + var self = this; + this.columnDefDialog = new ColumnDefDialog({grid: this.grid}); + + this.columnDefButton = new dijit.form.Button({ + label: "Display Columns", + type: "button", + iconClass: "columnDefDialogButtonIcon", + title: "Show/Hide Columns" + }); + + this.columnDefButton.on("click", function(e){ + _stopEvent(e); + self.columnDefDialog.showDialog(); + }); + + this.filterBar.addChild(this.columnDefButton); + }, + + _addFilteringTools: function(nls) + { + var self = this; + + this.filterDefButton = new dijit.form.Button({ + "class": "dojoxGridFBarBtn", + label: "Set Filter", + iconClass: "dojoxGridFBarDefFilterBtnIcon", + showLabel: "true", + title: "Define filter" + }); + + this.clearFilterButton = new dijit.form.Button({ + "class": "dojoxGridFBarBtn", + label: "Clear filter", + iconClass: "dojoxGridFBarClearFilterButtontnIcon", + showLabel: "true", + title: "Clear filter", + disabled: true + }); + + + this.filterDefButton.on("click", function(e){ + _stopEvent(e); + self.filterDefDialog.showDialog(); + }); + + this.clearFilterButton.on("click", function(e){ + _stopEvent(e); + if (self.ruleCountToConfirmClearFilter && self.filterDefDialog.getCriteria() >= self.ruleCountToConfirmClearFilter) + { + self.clearFilterDialog.show(); + } + else + { + self.grid.layer("filter").filterDef(null); + self.toggleClearFilterBtn(true) + } + }); + + this.filterSummary = new FilterSummary({grid: this.grid, filterStatusTip: this.filterStatusTip, nls: nls}); + + this.filterBar.addChild(this.filterDefButton); + this.filterBar.addChild(this.clearFilterButton); + + this.filterBar.addChild(new dijit.ToolbarSeparator()); + this.filterBar.addChild(this.filterSummary, "last"); + this.filterBar.getColumnIdx = function(coordX){return self.filterSummary._getColumnIdx(coordX);}; + + } + }); +});
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/FilterSummary.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/FilterSummary.js new file mode 100644 index 0000000000..2b1d960fa5 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/FilterSummary.js @@ -0,0 +1,173 @@ +/* + * + * 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. + * + */ + +define([ + "dojo/_base/declare", + "dojo/_base/lang", + "dojo/_base/html", + "dojo/query", + "dojo/dom-construct", + "dojo/string", + "dojo/on", + "dijit/_WidgetBase" +], function(declare, lang, html, query, domConstruct, string, on, _WidgetBase){ + +return declare("qpid.common.grid.FilterSummary", [_WidgetBase], { + + domNode: null, + itemName: null, + filterStatusTip: null, + grid: null, + _handle_statusTooltip: null, + _timeout_statusTooltip: 300, + _nls: null, + + constructor: function(params) + { + this.inherited(arguments); + this.itemName = params.itemsName; + this.initialize(params.filterStatusTip, params.grid); + this._nls = params.nls; + }, + + buildRendering: function(){ + this.inherited(arguments); + var itemsName = this.itemName || this._nls["defaultItemsName"]; + var message = string.substitute(this._nls["filterBarMsgNoFilterTemplate"], [0, itemsName ]); + this.domNode = domConstruct.create("span", {innerHTML: message, "class": "dijit dijitReset dijitInline dijitButtonInline", role: "presentation" }); + }, + + postCreate: function(){ + this.inherited(arguments); + on(this.domNode, "mouseenter", lang.hitch(this, this._onMouseEnter)); + on(this.domNode, "mouseleave", lang.hitch(this, this._onMouseLeave)); + on(this.domNode, "mousemove", lang.hitch(this, this._onMouseMove)); + }, + + destroy: function() + { + this.inherited(arguments); + this.itemName = null; + this.filterStatusTip = null; + this.grid = null; + this._handle_statusTooltip = null; + this._filteredClass = null; + this._nls = null; + }, + + initialize: function(filterStatusTip, grid) + { + this.filterStatusTip = filterStatusTip; + this.grid = grid; + if (this.grid) + { + var filterLayer = grid.layer("filter"); + this.connect(filterLayer, "onFiltered", this.onFiltered); + } + }, + + onFiltered: function(filteredSize, originSize) + { + try + { + var itemsName = this.itemName || this._nls["defaultItemsName"], + msg = "", g = this.grid, + filterLayer = g.layer("filter"); + if(filterLayer.filterDef()){ + msg = string.substitute(this._nls["filterBarMsgHasFilterTemplate"], [filteredSize, originSize, itemsName]); + }else{ + msg = string.substitute(this._nls["filterBarMsgNoFilterTemplate"], [originSize, itemsName]); + } + this.domNode.innerHTML = msg; + } + catch(e) + { + // swallow and log exception + // otherwise grid rendering is screwed + console.error(e); + } + }, + + _getColumnIdx: function(coordX){ + var headers = query("[role='columnheader']", this.grid.viewsHeaderNode); + var idx = -1; + for(var i = headers.length - 1; i >= 0; --i){ + var coord = html.position(headers[i]); + if(coordX >= coord.x && coordX < coord.x + coord.w){ + idx = i; + break; + } + } + if(idx >= 0 && this.grid.layout.cells[idx].filterable !== false){ + return idx; + }else{ + return -1; + } + }, + + _setStatusTipTimeout: function(){ + this._clearStatusTipTimeout(); + this._handle_statusTooltip = setTimeout(lang.hitch(this,this._showStatusTooltip),this._timeout_statusTooltip); + }, + + _clearStatusTipTimeout: function(){ + if (this._handle_statusTooltip){ + clearTimeout(this._handle_statusTooltip); + } + this._handle_statusTooltip = null; + }, + + _showStatusTooltip: function(){ + this._handle_statusTooltip = null; + if(this.filterStatusTip){ + this.filterStatusTip.showDialog(this._tippos.x, this._tippos.y, this._getColumnIdx(this._tippos.x)); + } + }, + + _updateTipPosition: function(evt){ + this._tippos = { + x: evt.pageX, + y: evt.pageY + }; + }, + + _onMouseEnter: function(e){ + this._updateTipPosition(e); + if(this.filterStatusTip){ + this._setStatusTipTimeout(); + } + }, + + _onMouseMove: function(e){ + if(this.filterStatusTip){ + this._setStatusTipTimeout(); + if(this._handle_statusTooltip){ + this._updateTipPosition(e); + } + } + }, + + _onMouseLeave: function(e){ + this._clearStatusTipTimeout(); + }, + }); + +});
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/GridUpdater.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/GridUpdater.js new file mode 100644 index 0000000000..7016e4eb5b --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/GridUpdater.js @@ -0,0 +1,258 @@ +/* + * + * 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. + * + */ + + +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/lang", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/UpdatableStore", + "qpid/common/util", + "dojo/store/Memory", + "dojo/data/ObjectStore", + "qpid/common/grid/EnhancedFilter", + "dojox/grid/enhanced/plugins/NestedSorting", + "dojo/domReady!"], + function (xhr, parser, array, lang, properties, updater, UpdatableStore, util, Memory, ObjectStore) { + + function GridUpdater(args, store) { + this.updatable = args.hasOwnProperty("updatable") ? args.updatable : true ; + this.serviceUrl = args.serviceUrl; + + this.onUpdate = args.onUpdate; + + this.appendData = args.append; + this.appendLimit = args.appendLimit; + this.initialData = args.data; + this.initializeStore(store); + }; + + GridUpdater.prototype.buildUpdatableGridArguments = function(args) + { + var filterPluginFound = args && args.hasOwnProperty("plugins") && args.plugins.filter ? true: false; + + var gridProperties = { + autoHeight: true, + plugins: { + pagination: { + defaultPageSize: 25, + pageSizes: [10, 25, 50, 100], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: "bottom" + }, + enhancedFilter: { + disableFiltering: filterPluginFound + } + } + }; + + if(args) + { + for(var argProperty in args) + { + if(args.hasOwnProperty(argProperty)) + { + if (argProperty == "plugins") + { + var argPlugins = args[ argProperty ]; + for(var argPlugin in argPlugins) + { + if(argPlugins.hasOwnProperty(argPlugin)) + { + var argPluginProperties = argPlugins[ argPlugin ]; + if (argPluginProperties && gridProperties.plugins.hasOwnProperty(argPlugin)) + { + var gridPlugin = gridProperties.plugins[ argPlugin ]; + for(var pluginProperty in argPluginProperties) + { + if(argPluginProperties.hasOwnProperty(pluginProperty)) + { + gridPlugin[pluginProperty] = argPluginProperties[pluginProperty]; + } + } + } + else + { + gridProperties.plugins[ argPlugin ] = argPlugins[ argPlugin ]; + } + } + } + } + else + { + gridProperties[ argProperty ] = args[ argProperty ]; + } + } + } + } + + gridProperties.updater = this; + gridProperties.store = this.dataStore; + + return gridProperties; + }; + + GridUpdater.prototype.initializeStore = function(store) + { + var self = this; + + function processData(data) + { + var dataSet = false; + if (!store) + { + store = new ObjectStore({objectStore: new Memory({data: data, idProperty: "id"})}); + dataSet = true; + } + self.dataStore = store + self.store = store; + if (store instanceof ObjectStore) + { + if( store.objectStore instanceof Memory) + { + self.memoryStore = store.objectStore; + } + self.store = store.objectStore + } + + if (data) + { + try + { + if ((dataSet || self.updateOrAppend(data)) && self.onUpdate) + { + self.onUpdate(data); + } + } + catch(e) + { + console.error(e); + } + } + }; + + if (this.serviceUrl) + { + var requestUrl = lang.isFunction(this.serviceUrl) ? this.serviceUrl() : this.serviceUrl; + xhr.get({url: requestUrl, sync: true, handleAs: "json"}).then(processData, util.errorHandler); + } + else + { + processData(this.initialData); + } + }; + + GridUpdater.prototype.start = function(grid) + { + this.grid = grid; + if (this.serviceUrl) + { + updater.add(this); + } + }; + + GridUpdater.prototype.destroy = function() + { + updater.remove(this); + if (this.dataStore) + { + this.dataStore.close(); + this.dataStore = null; + } + this.store = null; + this.memoryStore = null; + this.grid = null; + }; + + GridUpdater.prototype.updateOrAppend = function(data) + { + return this.appendData ? + UpdatableStore.prototype.append.call(this, data, this.appendLimit): + UpdatableStore.prototype.update.call(this, data); + }; + + GridUpdater.prototype.refresh = function(data) + { + this.updating = true; + try + { + if (this.updateOrAppend(data)) + { + // EnhancedGrid with Filter plugin has "filter" layer. + // The filter expression needs to be re-applied after the data update + var filterLayer = this.grid.layer("filter"); + if ( filterLayer && filterLayer.filterDef) + { + var currentFilter = filterLayer.filterDef(); + + if (currentFilter) + { + // re-apply filter in the filter layer + filterLayer.filterDef(currentFilter); + } + } + + // refresh grid to render updates + this.grid._refresh(); + } + } + finally + { + this.updating = false; + if (this.onUpdate) + { + this.onUpdate(data); + } + } + } + + GridUpdater.prototype.update = function() + { + if (this.updatable) + { + this.performUpdate(); + } + }; + + GridUpdater.prototype.performUpdate = function() + { + var self = this; + var requestUrl = lang.isFunction(this.serviceUrl) ? this.serviceUrl() : this.serviceUrl; + var requestArguments = {url: requestUrl, sync: properties.useSyncGet, handleAs: "json"}; + xhr.get(requestArguments).then(function(data){self.refresh(data);}); + }; + + GridUpdater.prototype.performRefresh = function(data) + { + if (!this.updating) + { + this.refresh(data); + } + }; + + return GridUpdater; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/RowNumberLimitDialog.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/RowNumberLimitDialog.js new file mode 100644 index 0000000000..db3ae5a2ea --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/RowNumberLimitDialog.js @@ -0,0 +1,96 @@ +/* + * + * 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. + * + */ + +define([ + "dojo/_base/declare", + "dojo/_base/event", + "dojo/_base/array", + "dojo/_base/lang", + "dojo/parser", + "dojo/dom-construct", + "dojo/query", + "dijit/registry", + "dijit/form/Button", + "dijit/form/CheckBox", + "dojox/grid/enhanced/plugins/Dialog", + "dojo/text!../../../grid/showRowNumberLimitDialog.html", + "dojo/domReady!" +], function(declare, event, array, lang, parser, dom, query, registry, Button, CheckBox, Dialog, template ){ + + +return declare("qpid.management.logs.RowNumberLimitDialog", null, { + + grid: null, + dialog: null, + + constructor: function(domNode, limitChangedCallback){ + + this.containerNode = dom.create("div", {innerHTML: template}); + parser.parse(this.containerNode); + + this.rowNumberLimit = registry.byNode(query(".rowNumberLimit", this.containerNode)[0]) + this.submitButton = registry.byNode(query(".submitButton", this.containerNode)[0]); + this.closeButton = registry.byNode(query(".cancelButton", this.containerNode)[0]); + + this.dialog = new Dialog({ + "refNode": domNode, + "title": "Grid Rows Number", + "content": this.containerNode + }); + + var self = this; + this.submitButton.on("click", function(e){ + if (self.rowNumberLimit.value > 0) + { + try + { + limitChangedCallback(self.rowNumberLimit.value); + } + catch(e) + { + console.error(e); + } + finally + { + self.dialog.hide(); + } + } + }); + + this.closeButton.on("click", function(e){self.dialog.hide(); }); + this.dialog.startup(); + }, + + destroy: function(){ + this.submitButton.destroy(); + this.closeButton.destroy(); + this.dialog.destroy(); + this.dialog = null; + }, + + showDialog: function(currentLimit){ + this.rowNumberLimit.set("value", currentLimit); + this.dialog.show(); + } + + }); + +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/UpdatableGrid.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/UpdatableGrid.js new file mode 100644 index 0000000000..04041388bd --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/grid/UpdatableGrid.js @@ -0,0 +1,56 @@ +/* + * + * 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. + * + */ + +define([ + "dojo/_base/declare", + "dojox/grid/EnhancedGrid", + "dojo/domReady!"], function(declare, EnhancedGrid){ + + return declare("qpid.common.grid.UpdatableGrid", [EnhancedGrid], { + + updater: null, + + postCreate: function(){ + this.inherited(arguments); + if (this.updater) + { + this.updater.start(this); + } + }, + + destroy: function(){ + if (this.updater) + { + try + { + this.updater.destroy(); + } + catch(e) + { + console.error(e) + } + this.updater = null; + } + this.inherited(arguments); + } + }); + +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js index 2c2096d390..3d349830ac 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/common/util.js @@ -77,10 +77,10 @@ define(["dojo/_base/xhr", return exchangeName == null || exchangeName == "" || "<<default>>" == exchangeName || exchangeName.indexOf("amq.") == 0 || exchangeName.indexOf("qpid.") == 0; }; - util.deleteGridSelections = function(updater, grid, url, confirmationMessageStart) + util.deleteGridSelections = function(updater, grid, url, confirmationMessageStart, idParam) { var data = grid.selection.getSelected(); - + var success = false; if(data.length) { var confirmationMessage = null; @@ -114,18 +114,19 @@ define(["dojo/_base/xhr", { queryParam = "?"; } - queryParam += "id=" + data[i].id; + queryParam += ( idParam || "id" ) + "=" + encodeURIComponent(data[i].id); } var query = url + queryParam; - var success = true var failureReason = ""; xhr.del({url: query, sync: true, handleAs: "json"}).then( function(data) { - // TODO why query *?? - //grid.setQuery({id: "*"}); + success = true; grid.selection.deselectAll(); - updater.update(); + if (updater) + { + updater.update(); + } }, function(error) {success = false; failureReason = error;}); if(!success ) @@ -134,6 +135,7 @@ define(["dojo/_base/xhr", } } } + return success; } util.isProviderManagingUsers = function(type) @@ -353,5 +355,21 @@ define(["dojo/_base/xhr", } }; + util.errorHandler = function errorHandler(error) + { + if(error.status == 401) + { + alert("Authentication Failed"); + } + else if(error.status == 403) + { + alert("Access Denied"); + } + else + { + alert(error); + } + } + return util; });
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js index 978ac4b45f..b5b4380a0d 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/AuthenticationProvider.js @@ -32,10 +32,13 @@ define(["dojo/_base/xhr", "dijit/registry", "dojo/dom-style", "dojox/html/entities", - "dojox/grid/enhanced/plugins/Pagination", - "dojox/grid/enhanced/plugins/IndirectSelection", + "dojo/dom", + "qpid/management/addPreferencesProvider", + "qpid/management/PreferencesProvider", + "qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager", "dojo/domReady!"], - function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid, addAuthenticationProvider, event, registry, domStyle, entities) { + function (xhr, parser, query, connect, properties, updater, util, UpdatableStore, EnhancedGrid, + addAuthenticationProvider, event, registry, domStyle, entities, dom, addPreferencesProvider, PreferencesProvider, PrincipalDatabaseAuthenticationManager) { function AuthenticationProvider(name, parent, controller) { this.name = name; @@ -48,7 +51,7 @@ define(["dojo/_base/xhr", } AuthenticationProvider.prototype.getTitle = function() { - return "AuthenticationProvider"; + return "AuthenticationProvider:" + this.name; }; AuthenticationProvider.prototype.open = function(contentPane) { @@ -62,8 +65,6 @@ define(["dojo/_base/xhr", that.authProviderUpdater = new AuthProviderUpdater(contentPane.containerNode, that.modelObj, that.controller, that); - updater.add( that.authProviderUpdater ); - that.authProviderUpdater.update(); var editButton = query(".editAuthenticationProviderButton", contentPane.containerNode)[0]; @@ -81,6 +82,15 @@ define(["dojo/_base/xhr", event.stop(evt); that.deleteAuthenticationProvider(); }); + + var addPreferencesProviderButton = query(".addPreferencesProviderButton", contentPane.containerNode)[0]; + var addPreferencesProviderWidget = registry.byNode(addPreferencesProviderButton); + connect.connect(addPreferencesProviderWidget, "onClick", + function(evt){ + event.stop(evt); + that.addPreferencesProvider(); + }); + updater.add( that.authProviderUpdater ); }}); }; @@ -111,6 +121,14 @@ define(["dojo/_base/xhr", } }; + AuthenticationProvider.prototype.addPreferencesProvider = function() { + if (this.authProviderUpdater && this.authProviderUpdater.authProviderData + && (!this.authProviderUpdater.authProviderData.preferencesproviders + || !this.authProviderUpdater.authProviderData.preferencesproviders[0])){ + addPreferencesProvider.show(this.name); + } + }; + function AuthProviderUpdater(node, authProviderObj, controller, authenticationProvider) { this.controller = controller; @@ -118,6 +136,13 @@ define(["dojo/_base/xhr", this.type = query(".type", node)[0]; this.state = query(".state", node)[0]; this.authenticationProvider = authenticationProvider; + this.preferencesProviderType=dom.byId("preferencesProviderType"); + this.preferencesProviderName=dom.byId("preferencesProviderName"); + this.preferencesProviderState=dom.byId("preferencesProviderState"); + this.addPreferencesProviderButton = query(".addPreferencesProviderButton", node)[0]; + this.editPreferencesProviderButton = query(".editPreferencesProviderButton", node)[0]; + this.deletePreferencesProviderButton = query(".deletePreferencesProviderButton", node)[0]; + this.preferencesProviderAttributes = dom.byId("preferencesProviderAttributes") this.query = "rest/authenticationprovider/" + encodeURIComponent(authProviderObj.name); @@ -139,16 +164,46 @@ define(["dojo/_base/xhr", if (util.isProviderManagingUsers(that.authProviderData.type)) { - require(["qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager"], - function(PrincipalDatabaseAuthenticationManager) { - that.details = new PrincipalDatabaseAuthenticationManager(node, data[0], controller, that); - that.details.update(); - }); + that.details = new PrincipalDatabaseAuthenticationManager(node, that.authProviderData, controller); + that.details.update(that.authProviderData); + } + if (that.authProviderData.type == "Anonymous") + { + var authenticationProviderPanel = registry.byNode( query(".preferencesPanel", node)[0]); + domStyle.set(authenticationProviderPanel.domNode, "display","none"); + } + else + { + var preferencesProviderData = that.authProviderData.preferencesproviders? that.authProviderData.preferencesproviders[0]: null; + that.preferencesNode = query(".preferencesProviderDetails", node)[0]; + that.updatePreferencesProvider(preferencesProviderData); } }); } + AuthProviderUpdater.prototype.updatePreferencesProvider = function(preferencesProviderData) + { + if (preferencesProviderData) + { + this.addPreferencesProviderButton.style.display = 'none'; + if (!this.preferencesProvider) + { + this.preferencesProvider=new PreferencesProvider(preferencesProviderData.name, this.authProviderData); + this.preferencesProvider.init(this.preferencesNode); + } + this.preferencesProvider.update(preferencesProviderData); + } + else + { + if (this.preferencesProvider) + { + this.preferencesProvider.update(null); + } + this.addPreferencesProviderButton.style.display = 'inline'; + } + }; + AuthProviderUpdater.prototype.updateHeader = function() { this.authenticationProvider.name = this.authProviderData[ "name" ] @@ -159,8 +214,44 @@ define(["dojo/_base/xhr", AuthProviderUpdater.prototype.update = function() { + var that = this; - var that = this; + xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + that.authProviderData = data[0]; + that.name = data[0].name + util.flattenStatistics( that.authProviderData ); + that.updateHeader(); + if (that.details) + { + try + { + that.details.update(that.authProviderData); + } + catch(e) + { + if (console) + { + console.error(e); + } + } + } + var preferencesProviderData = that.authProviderData.preferencesproviders? that.authProviderData.preferencesproviders[0]: null; + if (preferencesProviderData) + { + try + { + that.updatePreferencesProvider(preferencesProviderData); + } + catch(e) + { + if (console) + { + console.error(e); + } + } + } + }); }; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js index f721ad6fa5..1bb0ca0afa 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Broker.js @@ -352,6 +352,12 @@ define(["dojo/_base/xhr", that.brokerUpdater.update(); + var logViewerButton = query(".logViewer", contentPane.containerNode)[0]; + registry.byNode(logViewerButton).on("click", function(evt){ + that.controller.show("logViewer", null, null); + }); + + var addProviderButton = query(".addAuthenticationProvider", contentPane.containerNode)[0]; connect.connect(registry.byNode(addProviderButton), "onClick", function(evt){ addAuthenticationProvider.show(); }); @@ -664,43 +670,6 @@ define(["dojo/_base/xhr", }, gridProperties, EnhancedGrid); that.displayACLWarnMessage(aclData); }); - - xhr.get({url: "rest/logrecords", sync: properties.useSyncGet, handleAs: "json"}) - .then(function(data) - { - that.logData = data; - - var gridProperties = { - height: 400, - plugins: { - pagination: { - pageSizes: ["10", "25", "50", "100"], - description: true, - sizeSwitch: true, - pageStepper: true, - gotoButton: true, - maxPageStep: 4, - position: "bottom" - } - }}; - - - that.logfileGrid = - new UpdatableStore(that.logData, query(".broker-logfile")[0], - [ { name: "Timestamp", field: "timestamp", width: "200px", - formatter: function(val) { - var d = new Date(0); - d.setUTCSeconds(val/1000); - - return d.toLocaleString(); - }}, - { name: "Level", field: "level", width: "60px"}, - { name: "Logger", field: "logger", width: "280px"}, - { name: "Thread", field: "thread", width: "120px"}, - { name: "Log Message", field: "message", width: "100%"} - - ], null, gridProperties, EnhancedGrid); - }); } BrokerUpdater.prototype.updateHeader = function() @@ -805,15 +774,6 @@ define(["dojo/_base/xhr", that.displayACLWarnMessage(data); } }); - - - xhr.get({url: "rest/logrecords", sync: properties.useSyncGet, handleAs: "json"}) - .then(function(data) - { - that.logData = data; - that.logfileGrid.update(that.logData); - }); - }; BrokerUpdater.prototype.showReadOnlyAttributes = function() diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Preferences.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Preferences.js new file mode 100644 index 0000000000..735a657c61 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/Preferences.js @@ -0,0 +1,204 @@ +/* + * + * 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. + * + */ +define([ + "dojo/_base/declare", + "dojo/_base/xhr", + "dojo/_base/event", + "dojo/_base/connect", + "dojo/dom", + "dojo/dom-construct", + "dojo/parser", + "dojo/json", + "dojo/store/Memory", + "dojo/data/ObjectStore", + "dojox/html/entities", + "dijit/registry", + "qpid/common/TimeZoneSelector", + "dojo/text!../../showPreferences.html", + "qpid/common/util", + "dijit/Dialog", + "dijit/form/NumberSpinner", + "dijit/form/CheckBox", + "dijit/form/Textarea", + "dijit/form/FilteringSelect", + "dijit/form/TextBox", + "dijit/form/DropDownButton", + "dijit/form/Button", + "dijit/form/Form", + "dijit/layout/TabContainer", + "dijit/layout/ContentPane", + "dojox/grid/EnhancedGrid", + "dojox/validate/us", + "dojox/validate/web", + "dojo/domReady!"], +function (declare, xhr, event, connect, dom, domConstruct, parser, json, Memory, ObjectStore, entities, registry, TimeZoneSelector, markup, util) { + + var preferenceNames = ["timeZone", "updatePeriod", "saveTabs"]; + + return declare("qpid.management.Preferences", null, { + + preferencesDialog: null, + saveButton: null, + cancelButton: null, + + constructor: function() + { + var that = this; + + this.domNode = domConstruct.create("div", {innerHTML: markup}); + this.preferencesDialog = parser.parse(this.domNode)[0]; + + for(var i=0; i<preferenceNames.length; i++) + { + var name = preferenceNames[i]; + this[name] = registry.byId("preferences." + name); + } + + this.saveButton = registry.byId("preferences.saveButton"); + this.cancelButton = registry.byId("preferences.cancelButton"); + this.theForm = registry.byId("preferences.preferencesForm"); + this.users = registry.byId("preferences.users"); + this.users.set("structure", [ { name: "User", field: "name", width: "50%"}, + { name: "Authentication Provider", field: "authenticationProvider", width: "50%"}]); + this.cancelButton.on("click", function(){that.preferencesDialog.hide();}); + this.deletePreferencesButton = registry.byId("preferences.deletePreeferencesButton"); + this.deletePreferencesButton.on("click", function(){ + if (util.deleteGridSelections( + null, + that.users, + "rest/userpreferences", + "Are you sure you want to delete preferences for user", + "user")) + { + that._updateUsersWithPreferences(); + } + }); + var deletePreferencesButtonToggler = function(rowIndex){ + var data = that.users.selection.getSelected(); + that.deletePreferencesButton.set("disabled",!data.length ); + }; + connect.connect(this.users.selection, 'onSelected', deletePreferencesButtonToggler); + connect.connect(this.users.selection, 'onDeselected', deletePreferencesButtonToggler); + this.theForm.on("submit", function(e){ + event.stop(e); + if(that.theForm.validate()){ + var preferences = {}; + for(var i=0; i<preferenceNames.length; i++) + { + var name = preferenceNames[i]; + var preferenceWidget = that[name]; + if (preferenceWidget) + { + preferences[name] = preferenceWidget.get("value"); + } + } + xhr.post({ + url: "rest/preferences", + sync: true, + handleAs: "json", + headers: { "Content-Type": "application/json"}, + postData: json.stringify(preferences), + load: function(x) {that.success = true; }, + error: function(error) {that.success = false; that.failureReason = error;} + }); + if(that.success === true) + { + that.preferencesDialog.hide(); + } + else + { + alert("Error:" + that.failureReason); + } + } + return false; + }); + this.preferencesDialog.startup(); + }, + + showDialog: function(){ + var that = this; + xhr.get({ + url: "rest/preferences", + sync: true, + handleAs: "json", + load: function(data) { + that._updatePreferencesWidgets(data); + that._updateUsersWithPreferences(); + that.preferencesDialog.show(); + }, + error: function(error){ + alert("Cannot load user preferences : " + error); + } + }); + }, + + destroy: function() + { + if (this.preferencesDialog) + { + this.preferencesDialog.destroyRecursevly(); + this.preferencesDialog = null; + } + }, + + _updatePreferencesWidgets: function(data) + { + for(var i=0; i<preferenceNames.length; i++) + { + var preference = preferenceNames[i]; + if (this.hasOwnProperty(preference)) + { + var value = data ? data[preference] : null; + if (typeof value == "string") + { + value = entities.encode(String(value)) + } + this[preference].set("value", value); + } + } + }, + + _updateUsersWithPreferences: function() + { + var that = this; + xhr.get({ + url: "rest/userpreferences", + sync: false, + handleAs: "json" + }).then( + function(users) { + for(var i=0; i<users.length; i++) + { + users[i].id = users[i].authenticationProvider + "/" + users[i].name; + } + var usersStore = new Memory({data: users, idProperty: "id"}); + var usersDataStore = new ObjectStore({objectStore: usersStore}); + if (that.users.store) + { + that.users.store.close(); + } + that.users.set("store", usersDataStore); + that.users._refresh(); + }); + } + + }); +});
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/PreferencesProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/PreferencesProvider.js new file mode 100644 index 0000000000..c8e6f9845c --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/PreferencesProvider.js @@ -0,0 +1,179 @@ +/* + * + * 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. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/_base/connect", + "qpid/common/properties", + "qpid/common/updater", + "qpid/common/util", + "dojo/_base/event", + "dijit/registry", + "dojo/dom-style", + "dojox/html/entities", + "qpid/management/addPreferencesProvider", + "dojo/domReady!"], + function (xhr, parser, query, connect, properties, updater, util, event, registry, domStyle, entities, addPreferencesProvider) { + + function PreferencesProvider(name, parent, controller) { + this.name = name; + this.controller = controller; + this.modelObj = { type: "preferencesprovider", name: name }; + this.authenticationProviderName = parent.name; + if(parent) { + this.modelObj.parent = {}; + this.modelObj.parent[parent.type] = parent; + } + } + + PreferencesProvider.prototype.getTitle = function() { + return "PreferencesProvider:" + this.authenticationProviderName + "/" + this.name ; + }; + + PreferencesProvider.prototype.init = function(node) { + var that = this; + xhr.get({url: "showPreferencesProvider.html", + sync: true, + load: function(data) { + node.innerHTML = data; + parser.parse(node); + + that.preferencesProviderType=query(".preferencesProviderType", node)[0]; + that.preferencesProviderName=query(".preferencesProviderName", node)[0]; + that.preferencesProviderState=query(".preferencesProviderState", node)[0]; + that.editPreferencesProviderButton = query(".editPreferencesProviderButton", node)[0]; + that.deletePreferencesProviderButton = query(".deletePreferencesProviderButton", node)[0]; + that.preferencesProviderAttributes = query(".preferencesProviderAttributes", node)[0]; + that.preferencesDetailsDiv = query(".preferencesDetails", node)[0]; + var editPreferencesProviderWidget = registry.byNode(that.editPreferencesProviderButton); + editPreferencesProviderWidget.on("click", function(evt){ event.stop(evt); that.editPreferencesProvider();}); + var deletePreferencesProviderWidget = registry.byNode(that.deletePreferencesProviderButton); + deletePreferencesProviderWidget.on("click", function(evt){ event.stop(evt); that.deletePreferencesProvider();}); + }}); + this.reload(); + }; + + PreferencesProvider.prototype.open = function(contentPane) { + this.contentPane = contentPane; + this.init(contentPane.containerNode); + this.updater = new PreferencesProviderUpdater(this); + updater.add(this.updater); + }; + + PreferencesProvider.prototype.close = function() { + if (this.updater) + { + updater.remove( this.updater); + } + }; + + PreferencesProvider.prototype.deletePreferencesProvider = function() { + if (this.preferencesProviderData){ + var preferencesProviderData = this.preferencesProviderData; + if(confirm("Are you sure you want to delete preferences provider '" + preferencesProviderData.name + "'?")) { + var query = "rest/preferencesprovider/" + encodeURIComponent(this.authenticationProviderName) + "/" + encodeURIComponent(preferencesProviderData.name); + this.success = true + var that = this; + xhr.del({url: query, sync: true, handleAs: "json"}).then( + function(data) { + that.update(null); + + // if opened in tab + if (that.contentPane) + { + that.close(); + that.contentPane.onClose() + that.controller.tabContainer.removeChild(that.contentPane); + that.contentPane.destroyRecursive(); + } + }, + function(error) {that.success = false; that.failureReason = error;}); + if(!this.success ) { + alert("Error:" + this.failureReason); + } + } + } + }; + + PreferencesProvider.prototype.editPreferencesProvider = function() { + if (this.preferencesProviderData){ + addPreferencesProvider.show(this.authenticationProviderName, this.name); + } + }; + + PreferencesProvider.prototype.update = function(data) { + this.preferencesProviderData = data; + if (data) + { + this.name = data.name; + this.preferencesProviderAttributes.style.display = 'block'; + this.editPreferencesProviderButton.style.display = 'inline'; + this.deletePreferencesProviderButton.style.display = 'inline'; + this.preferencesProviderType.innerHTML = entities.encode(String(data.type)); + this.preferencesProviderName.innerHTML = entities.encode(String(data.name)); + this.preferencesProviderState.innerHTML = entities.encode(String(data.state)); + if (!this.details) + { + var that = this; + require(["qpid/management/authenticationprovider/preferences/" + data.type.toLowerCase() + "/show"], + function(PreferencesProviderDetails) { + that.details = new PreferencesProviderDetails(that.preferencesDetailsDiv); + that.details.update(data); + }); + } + else + { + this.details.update(data); + } + } + else + { + this.editPreferencesProviderButton.style.display = 'none'; + this.deletePreferencesProviderButton.style.display = 'none'; + this.preferencesProviderAttributes.style.display = 'none'; + this.details = null; + } + }; + + PreferencesProvider.prototype.reload = function() + { + var query = "rest/preferencesprovider/" + encodeURIComponent(this.authenticationProviderName) + "/" + encodeURIComponent(this.name); + var that = this; + xhr.get({url: query, sync: properties.useSyncGet, handleAs: "json"}) + .then(function(data) { + var preferencesProviderData = data[0]; + util.flattenStatistics( preferencesProviderData ); + that.update(preferencesProviderData); + }); + }; + + function PreferencesProviderUpdater(preferencesProvider) + { + this.preferencesProvider = preferencesProvider; + }; + + PreferencesProviderUpdater.prototype.update = function() + { + this.preferencesProvider.reload(); + }; + + return PreferencesProvider; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js index d2891c7d3b..3737e41da4 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addAuthenticationProvider.js @@ -31,6 +31,7 @@ define(["dojo/_base/xhr", "dijit/form/FilteringSelect", "dojo/_base/connect", "dojo/dom-style", + "qpid/management/addPreferencesProvider", /* dojox/ validate resources */ "dojox/validate/us", "dojox/validate/web", /* basic dijit classes */ @@ -44,7 +45,7 @@ define(["dojo/_base/xhr", "dojox/form/BusyButton", "dojox/form/CheckedMultiSelect", "dojox/layout/TableContainer", "dojo/domReady!"], - function (xhr, dom, construct, win, registry, parser, array, event, json, Memory, FilteringSelect, connect, domStyle) { + function (xhr, dom, construct, win, registry, parser, array, event, json, Memory, FilteringSelect, connect, domStyle, addPreferencesProvider) { var addAuthenticationProvider = {}; @@ -163,6 +164,10 @@ define(["dojo/_base/xhr", if(this.success === true) { registry.byId("addAuthenticationProvider").hide(); + if (newAuthenticationManager.type != "Anonymous" && dojo.byId("formAddAuthenticationProvider.id").value == "") + { + addPreferencesProvider.show(newAuthenticationManager.name); + } } else { diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPreferencesProvider.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPreferencesProvider.js new file mode 100644 index 0000000000..818dc32366 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/addPreferencesProvider.js @@ -0,0 +1,198 @@ +/* + * + * 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. + * + */ +define(["dojo/_base/xhr", + "dojo/dom", + "dojo/dom-construct", + "dojo/query", + "dojo/_base/window", + "dijit/registry", + "dojo/parser", + "dojo/_base/array", + "dojo/_base/event", + 'dojo/_base/json', + "dojo/store/Memory", + "dijit/form/FilteringSelect", + "dojo/_base/connect", + "dojo/dom-style", + "dojo/string", + "dojox/html/entities", + "dojox/validate/us", + "dojox/validate/web", + "dijit/Dialog", + "dijit/form/CheckBox", + "dijit/form/Textarea", + "dijit/form/TextBox", + "dijit/form/ValidationTextBox", + "dijit/form/Button", + "dijit/form/Form", + "dojox/form/BusyButton", + "dojox/form/CheckedMultiSelect", + "dojox/layout/TableContainer", + "dojo/domReady!"], + function (xhr, dom, construct, query, win, registry, parser, array, event, json, Memory, FilteringSelect, connect, domStyle, string, entities) { + + var addPreferencesProvider = {}; + + var node = construct.create("div", null, win.body(), "last"); + + var convertToPreferencesProvider = function convertToPreferencesProvider(formValues) + { + var newProvider = {}; + + newProvider.name = dijit.byId("preferencesProvider.name").value; + newProvider.type = dijit.byId("preferencesProvider.type").value; + var id = dojo.byId("preferencesProvider.id").value; + if (id) + { + newProvider.id = id; + } + for(var propName in formValues) + { + if(formValues.hasOwnProperty(propName)) + { + if(formValues[ propName ] !== "") { + newProvider[ propName ] = formValues[propName]; + } + + } + } + return newProvider; + } + + var selectPreferencesProviderType = function(type) { + if(type && string.trim(type) != "") + { + require(["qpid/management/authenticationprovider/preferences/" + type.toLowerCase() + "/add"], + function(addType) + { + addType.show(dom.byId("preferencesProvider.fieldsContainer"), addPreferencesProvider.data) + }); + } + } + + xhr.get({url: "addPreferencesProvider.html", + sync: true, + load: function(data) { + node.innerHTML = data; + addPreferencesProvider.dialogNode = dom.byId("addPreferencesProvider"); + parser.instantiate([addPreferencesProvider.dialogNode]); + + var cancelButton = registry.byId("addPreferencesProvider.cancelButton"); + cancelButton.on("click", function(){ + registry.byId("addPreferencesProvider").hide(); + }); + var theForm = registry.byId("formAddPreferencesProvider"); + theForm.on("submit", function(e) { + + event.stop(e); + if(theForm.validate()){ + var newProvider = convertToPreferencesProvider(theForm.getValues()); + var that = this; + var nameWidget = registry.byId("preferencesProvider.name") + xhr.put({url: "rest/preferencesprovider/" +encodeURIComponent(addPreferencesProvider.authenticationProviderName) + "/" + encodeURIComponent(nameWidget.value), + sync: true, handleAs: "json", + headers: { "Content-Type": "application/json"}, + putData: json.toJson(newProvider), + load: function(x) {that.success = true; }, + error: function(error) {that.success = false; that.failureReason = error;}}); + if(this.success === true) + { + registry.byId("addPreferencesProvider").hide(); + } + else + { + alert("Error:" + this.failureReason); + } + return false; + }else{ + alert('Form contains invalid data. Please correct first'); + return false; + } + }); + xhr.get({ + sync: true, + url: "rest/helper?action=ListPreferencesProvidersTypes", + handleAs: "json" + }).then( + function(data) { + var preferencesProvidersTypes = data; + var storeData = []; + for (var i =0 ; i < preferencesProvidersTypes.length; i++) + { + storeData[i]= {id: preferencesProvidersTypes[i], name: preferencesProvidersTypes[i]}; + } + var store = new Memory({ data: storeData }); + var preferencesProviderTypesDiv = dom.byId("addPreferencesProvider.selectPreferencesProviderDiv"); + var input = construct.create("input", {id: "preferencesProviderType", required: true}, preferencesProviderTypesDiv); + addPreferencesProvider.preferencesProviderTypeChooser = new FilteringSelect({ id: "preferencesProvider.type", + name: "type", + store: store, + searchAttr: "name", + required: true, + onChange: selectPreferencesProviderType }, input); + addPreferencesProvider.preferencesProviderTypeChooser.startup(); + }); + }}); + + addPreferencesProvider.show = function(authenticationProviderName, providerName) { + this.authenticationProviderName = authenticationProviderName; + this.data = null; + var that = this; + var theForm = registry.byId("formAddPreferencesProvider"); + theForm.reset(); + dojo.byId("preferencesProvider.id").value=""; + var nameWidget = registry.byId("preferencesProvider.name"); + nameWidget.set("disabled", false); + registry.byId("preferencesProvider.type").set("disabled", false); + if (this.preferencesProviderTypeChooser) + { + this.preferencesProviderTypeChooser.set("disabled", false); + this.preferencesProviderTypeChooser.set("value", null); + } + var dialog = registry.byId("addPreferencesProvider"); + dialog.set("title", (providerName ? "Edit preference provider '" + entities.encode(String(providerName)) + "' " : "Add preferences provider ") + " for authentication provider '" + entities.encode(String(authenticationProviderName)) + "' ") + if (providerName) + { + xhr.get({ + url: "rest/preferencesprovider/" +encodeURIComponent(authenticationProviderName) + "/" + encodeURIComponent(providerName), + sync: false, + handleAs: "json" + }).then( + function(data) { + var provider = data[0]; + var providerType = provider.type; + that.data = provider; + nameWidget.set("value", entities.encode(String(provider.name))); + nameWidget.set("disabled", true); + that.preferencesProviderTypeChooser.set("value", providerType); + that.preferencesProviderTypeChooser.set("disabled", true); + dojo.byId("preferencesProvider.id").value=provider.id; + dialog.show(); + }); + } + else + { + dialog.show(); + } + } + + return addPreferencesProvider; + });
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js index 0a607c71d4..09433b196d 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/PrincipalDatabaseAuthenticationManager.js @@ -30,7 +30,6 @@ define(["dojo/_base/xhr", "dijit/registry", "qpid/common/util", "qpid/common/properties", - "qpid/common/updater", "qpid/common/UpdatableStore", "dojox/grid/EnhancedGrid", "dojox/grid/enhanced/plugins/Pagination", @@ -43,8 +42,8 @@ define(["dojo/_base/xhr", "dijit/form/Form", "dijit/form/DateTextBox", "dojo/domReady!"], - function (xhr, dom, parser, query, construct, connect, win, event, json, registry, util, properties, updater, UpdatableStore, EnhancedGrid) { - function DatabaseAuthManager(containerNode, authProviderObj, controller, authenticationManagerUpdater) { + function (xhr, dom, parser, query, construct, connect, win, event, json, registry, util, properties, UpdatableStore, EnhancedGrid) { + function DatabaseAuthManager(containerNode, authProviderObj, controller) { var node = construct.create("div", null, containerNode, "last"); var that = this; this.name = authProviderObj.name; @@ -53,14 +52,7 @@ define(["dojo/_base/xhr", load: function(data) { node.innerHTML = data; parser.parse(node); - - - that.authDatabaseUpdater= new AuthProviderUpdater(node, authProviderObj, controller, authenticationManagerUpdater); - updater.add( that.authDatabaseUpdater); - - that.authDatabaseUpdater.update(); - - + that.init(node, authProviderObj, controller); }}); } @@ -72,19 +64,12 @@ define(["dojo/_base/xhr", updater.remove( this.authDatabaseUpdater ); }; - function AuthProviderUpdater(node, authProviderObj, controller, authenticationManagerUpdater) + DatabaseAuthManager.prototype.init = function(node, authProviderObj, controller) { this.controller = controller; - this.query = "rest/authenticationprovider?id="+encodeURIComponent(authProviderObj.id); - this.name = authProviderObj.name; - this.authenticationManagerUpdater = authenticationManagerUpdater; var that = this; - xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) - .then(function(data) { - that.authProviderData = data[0]; - that.name = data[0].name - util.flattenStatistics( that.authProviderData ); + that.authProviderData = authProviderObj; var userDiv = query(".users")[0]; @@ -131,10 +116,9 @@ define(["dojo/_base/xhr", event.stop(evt); that.deleteUsers(); }); - }); } - AuthProviderUpdater.prototype.deleteUsers = function() + DatabaseAuthManager.prototype.deleteUsers = function() { var grid = this.usersGrid.grid; var data = grid.selection.getSelected(); @@ -168,24 +152,11 @@ define(["dojo/_base/xhr", } }; - AuthProviderUpdater.prototype.update = function() + DatabaseAuthManager.prototype.update = function(data) { - - var that = this; - - xhr.get({url: this.query, sync: properties.useSyncGet, handleAs: "json"}) - .then(function(data) { - that.authProviderData = data[0]; - that.name = data[0].name - util.flattenStatistics( that.authProviderData ); - - that.usersGrid.update(that.authProviderData.users); - - that.authenticationManagerUpdater.authProviderData = data[0]; - that.authenticationManagerUpdater.updateHeader(); - }); - - + this.authProviderData = data; + this.name = data.name + this.usersGrid.update(this.authProviderData.users); }; var addUser = {}; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/virtualhost/store/memory/add.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/preferences/filesystempreferences/add.js index 3a9b23274d..80b50fbbb8 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/virtualhost/store/memory/add.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/preferences/filesystempreferences/add.js @@ -28,29 +28,22 @@ define(["dojo/_base/xhr", "dojo/_base/event", "dojo/_base/json", "dojo/string", - "dojo/store/Memory", - "dijit/form/FilteringSelect", + "dojox/html/entities", + "dojo/text!../../../../../authenticationprovider/preferences/filesystempreferences/add.html", "dojo/domReady!"], - function (xhr, dom, construct, win, registry, parser, array, event, json, string, Memory, FilteringSelect) { + function (xhr, dom, domConstruct, win, registry, parser, array, event, json, string, entities, template) { return { - show: function() { - var node = dom.byId("addVirtualHost.storeSpecificDiv"); - var that = this; - - array.forEach(registry.toArray(), - function(item) { - if(item.id.substr(0,33) == "formAddVirtualHost.specific.store") { - item.destroyRecursive(); - } - }); - - xhr.get({url: "virtualhost/store/memory/add.html", - sync: true, - load: function(data) { - node.innerHTML = data; - parser.parse(node); - - }}); + show: function(node, data) { + dojo.forEach(dijit.findWidgets(node), function(w) { + w.destroyRecursive(); + }); + node.innerHTML = template; + parser.parse(node); + var pathWidget = registry.byId("preferencesProvider.path") + if (data) + { + pathWidget.set("value", entities.encode(String(data["path"]))); + } } }; }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/preferences/filesystempreferences/show.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/preferences/filesystempreferences/show.js new file mode 100644 index 0000000000..7521f820b9 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/authenticationprovider/preferences/filesystempreferences/show.js @@ -0,0 +1,46 @@ +/* + * + * 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. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/string", + "dojox/html/entities", + "dojo/query", + "dojo/domReady!"], + function (xhr, parser, json, entities, query) { + + function FileSystemPreferences(containerNode) { + var that = this; + xhr.get({url: "authenticationprovider/preferences/filesystempreferences/show.html", + sync: true, + load: function(template) { + containerNode.innerHTML = template; + parser.parse(containerNode); + that.preferencesProviderPath=query(".fileSystemPreferencesProviderPath", containerNode)[0]; + }}); + } + + FileSystemPreferences.prototype.update=function(data) + { + this.preferencesProviderPath.innerHTML = entities.encode(String(data["path"])); + }; + + return FileSystemPreferences; +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js index b7eddbbb77..e8d7bdd9cf 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/controller.js @@ -35,10 +35,12 @@ define(["dojo/dom", "qpid/management/AccessControlProvider", "qpid/management/Port", "qpid/management/Plugin", + "qpid/management/logs/LogViewer", + "qpid/management/PreferencesProvider", "dojo/ready", "dojo/domReady!"], function (dom, registry, ContentPane, entities, Broker, VirtualHost, Exchange, Queue, Connection, AuthProvider, - GroupProvider, Group, KeyStore, TrustStore, AccessControlProvider, Port, Plugin, ready) { + GroupProvider, Group, KeyStore, TrustStore, AccessControlProvider, Port, Plugin, LogViewer, PreferencesProvider, ready) { var controller = {}; var constructors = { broker: Broker, virtualhost: VirtualHost, exchange: Exchange, @@ -46,7 +48,7 @@ define(["dojo/dom", authenticationprovider: AuthProvider, groupprovider: GroupProvider, group: Group, keystore: KeyStore, truststore: TrustStore, accesscontrolprovider: AccessControlProvider, port: Port, - plugin: Plugin}; + plugin: Plugin, logViewer: LogViewer, preferencesprovider: PreferencesProvider}; var tabDiv = dom.byId("managedViews"); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/logs/LogFileDownloadDialog.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/logs/LogFileDownloadDialog.js new file mode 100644 index 0000000000..c25fd7c609 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/logs/LogFileDownloadDialog.js @@ -0,0 +1,175 @@ +/* + * + * 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. + * + */ +define([ + "dojo/_base/declare", + "dojo/_base/event", + "dojo/_base/xhr", + "dojo/_base/connect", + "dojo/dom-construct", + "dojo/query", + "dojo/parser", + "dojo/store/Memory", + "dojo/data/ObjectStore", + "dojo/date/locale", + "dojo/number", + "dijit/registry", + "dijit/Dialog", + "dijit/form/Button", + "dojox/grid/EnhancedGrid", + "dojo/text!../../../logs/showLogFileDownloadDialog.html", + "dojo/domReady!" +], function(declare, event, xhr, connect, domConstruct, query, parser, Memory, ObjectStore, locale, number, + registry, Dialog, Button, EnhancedGrid, template){ + + +return declare("qpid.management.logs.LogFileDownloadDialog", null, { + + templateString: template, + containerNode: null, + widgetsInTemplate: true, + logFileDialog: null, + logFilesGrid: null, + downloadLogsButton: null, + closeButton: null, + + constructor: function(args){ + this.containerNode = domConstruct.create("div", {innerHTML: template}); + parser.parse(this.containerNode); + + this.logFileTreeDiv = query(".logFilesGrid", this.containerNode)[0]; + this.downloadLogsButton = registry.byNode(query(".downloadLogsButton", this.containerNode)[0]); + this.closeButton = registry.byNode(query(".downloadLogsDialogCloseButton", this.containerNode)[0]); + + var self = this; + this.closeButton.on("click", function(e){self._onCloseButtonClick(e);}); + this.downloadLogsButton.on("click", function(e){self._onDownloadButtonClick(e);}); + this.downloadLogsButton.set("disabled", true) + + this.logFileDialog = new Dialog({ + title:"Broker Log Files", + style: "width: 600px", + content: this.containerNode + }); + + var layout = [ + { name: "Appender", field: "appenderName", width: "auto"}, + { name: "Name", field: "name", width: "auto"}, + { name: "Size", field: "size", width: "60px", + formatter: function(val){ + return val > 1024 ? (val > 1048576? number.round(val/1048576) + "MB": number.round(val/1024) + "KB") : val + "bytes"; + } + }, + { name: "Last Modified", field: "lastModified", width: "250px", + formatter: function(val) { + var d = new Date(val); + return locale.format(d, {selector:"date", datePattern: "EEE, MMM d yy, HH:mm:ss z (ZZZZ)"}); + } + } + ]; + + var gridProperties = { + store: new ObjectStore({objectStore: new Memory({data: [], idProperty: "id"}) }), + structure: layout, + autoHeight: true, + plugins: { + pagination: { + pageSizes: [10, 25, 50, 100], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: "bottom" + }, + indirectSelection: { + headerSelector:true, + width:"20px", + styles:"text-align: center;" + } + } + }; + + this.logFilesGrid = new EnhancedGrid(gridProperties, this.logFileTreeDiv); + var self = this; + var downloadButtonToggler = function(rowIndex){ + var data = self.logFilesGrid.selection.getSelected(); + self.downloadLogsButton.set("disabled",!data.length ); + }; + connect.connect(this.logFilesGrid.selection, 'onSelected', downloadButtonToggler); + connect.connect(this.logFilesGrid.selection, 'onDeselected', downloadButtonToggler); + }, + + _onCloseButtonClick: function(evt){ + event.stop(evt); + this.logFileDialog.hide(); + }, + + _onDownloadButtonClick: function(evt){ + event.stop(evt); + var data = this.logFilesGrid.selection.getSelected(); + if (data.length) + { + var query = ""; + for(var i = 0 ; i< data.length; i++) + { + if (i>0) + { + query+="&"; + } + query+="l="+encodeURIComponent(data[i].appenderName +'/' + data[i].name); + } + window.location="rest/logfile?" + query; + this.logFileDialog.hide(); + } + }, + + destroy: function(){ + this.inherited(arguments); + if (this.logFileDialog) + { + this.logFileDialog.destroyRecursive(); + this.logFileDialog = null; + } + }, + + showDialog: function(){ + var self = this; + var requestArguments = {url: "rest/logfiles", sync: true, handleAs: "json"}; + xhr.get(requestArguments).then(function(data){ + try + { + self.logFilesGrid.store.objectStore.setData(data); + self.logFilesGrid.startup(); + self.logFileDialog.startup(); + self.logFileDialog.show(); + self.logFilesGrid._refresh(); + + } + catch(e) + { + console.error(e); + } + }); + } + + }); + +}); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/logs/LogViewer.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/logs/LogViewer.js new file mode 100644 index 0000000000..56b37d0167 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/logs/LogViewer.js @@ -0,0 +1,199 @@ +/* + * + * 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. + * + */ +define(["dojo/_base/xhr", + "dojo/parser", + "dojo/query", + "dojo/date/locale", + "dijit/registry", + "qpid/common/grid/GridUpdater", + "qpid/common/grid/UpdatableGrid", + "qpid/management/logs/LogFileDownloadDialog", + "dojo/text!../../../logs/showLogViewer.html", + "dojo/domReady!"], + function (xhr, parser, query, locale, registry, GridUpdater, UpdatableGrid, LogFileDownloadDialog, markup) { + + var defaulGridRowLimit = 4096; + + function LogViewer(name, parent, controller) { + var self = this; + + this.name = name; + this.lastLogId = 0; + this.contentPane = null; + this.downloadLogsButton = null; + this.downloadLogDialog = null; + } + + LogViewer.prototype.getTitle = function() { + return "Log Viewer"; + }; + + LogViewer.prototype.open = function(contentPane) { + var self = this; + this.contentPane = contentPane; + this.contentPane.containerNode.innerHTML = markup; + + parser.parse(this.contentPane.containerNode); + + this.downloadLogsButton = registry.byNode(query(".downloadLogs", contentPane.containerNode)[0]); + this.downloadLogDialog = new LogFileDownloadDialog(); + + this.downloadLogsButton.on("click", function(evt){ + self.downloadLogDialog.showDialog(); + }); + this._buildGrid(); + }; + + LogViewer.prototype._buildGrid = function() { + var self = this; + + var gridStructure = [ + { + hidden: true, + name: "ID", + field: "id", + width: "50px", + datatype: "number", + filterable: true + }, + { + name: "Date", field: "timestamp", width: "100px", datatype: "date", + formatter: function(val) { + var d = new Date(0); + d.setUTCSeconds(val/1000); + return locale.format(d, {selector:"date", datePattern: "EEE, MMM d yy"}); + }, + dataTypeArgs: { + selector: "date", + datePattern: "EEE MMMM d yyy" + } + }, + { name: "Time", field: "timestamp", width: "150px", datatype: "time", + formatter: function(val) { + var d = new Date(0); + d.setUTCSeconds(val/1000); + return locale.format(d, {selector:"time", timePattern: "HH:mm:ss z (ZZZZ)"}); + }, + dataTypeArgs: { + selector: "time", + timePattern: "HH:mm:ss ZZZZ" + } + }, + { name: "Level", field: "level", width: "50px", datatype: "string", autoComplete: true, hidden: true}, + { name: "Logger", field: "logger", width: "150px", datatype: "string", autoComplete: false, hidden: true}, + { name: "Thread", field: "thread", width: "100px", datatype: "string", hidden: true}, + { name: "Log Message", field: "message", width: "auto", datatype: "string"} + ]; + + var gridNode = query("#broker-logfile", this.contentPane.containerNode)[0]; + try + { + var updater = new GridUpdater({ + updatable: false, + serviceUrl: function() + { + return "rest/logrecords?lastLogId=" + self.lastLogId; + }, + onUpdate: function(items) + { + if (items) + { + var maxId = -1; + for(var i in items) + { + var item = items[i]; + if (item.id > maxId) + { + maxId = item.id + } + } + if (maxId != -1) + { + self.lastLogId = maxId + } + } + }, + append: true, + appendLimit: defaulGridRowLimit + }); + this.grid = new UpdatableGrid(updater.buildUpdatableGridArguments({ + structure: gridStructure, + selectable: true, + selectionMode: "none", + sortInfo: -1, + sortFields: [{attribute: 'timestamp', descending: true}], + plugins:{ + nestedSorting:true, + enhancedFilter:{defaulGridRowLimit: defaulGridRowLimit}, + indirectSelection: false + } + }), gridNode); + var onStyleRow = function(row) + { + var item = self.grid.getItem(row.index); + if(item){ + var level = self.grid.store.getValue(item, "level", null); + var changed = false; + if(level == "ERROR"){ + row.customClasses += " redBackground"; + changed = true; + } else if(level == "WARN"){ + row.customClasses += " yellowBackground"; + changed = true; + } else if(level == "DEBUG"){ + row.customClasses += " grayBackground"; + changed = true; + } + if (changed) + { + self.grid.focus.styleRow(row); + } + } + }; + this.grid.on("styleRow", onStyleRow); + this.grid.startup(); + } + catch(err) + { + console.error(err); + } + }; + + LogViewer.prototype.close = function() { + if (this.grid) + { + this.grid.destroy(); + this.grid = null; + } + if (this.downloadLogDialog) + { + this.downloadLogDialog.destroy(); + this.downloadLogDialog = null; + } + if (this.downloadLogsButton) + { + this.downloadLogsButton.destroy(); + this.downloadLogsButton = null; + } + }; + + return LogViewer; + }); diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js index b1ccc0ca07..59822ec535 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/showMessage.js @@ -37,15 +37,10 @@ define(["dojo/_base/xhr", return typeof val === 'string' ? entities.encode(val) : val; } + var populatedFields = []; var showMessage = {}; showMessage.hide = function () { - if(this.populatedFields) { - for(var i = 0 ; i < this.populatedFields.length; i++) { - this.populatedFields[i].innerHTML = ""; - } - this.populatedFields = []; - } registry.byId("showMessage").hide(); }; @@ -65,16 +60,22 @@ define(["dojo/_base/xhr", showMessage.populateShowMessage = function(data) { - this.populatedFields = []; + // clear fields set by previous invocation. + if(populatedFields) { + for(var i = 0 ; i < populatedFields.length; i++) { + populatedFields[i].innerHTML = ""; + } + populatedFields = []; + } for(var attrName in data) { if(data.hasOwnProperty(attrName)) { var fields = query(".message-"+attrName, this.dialogNode); if(fields && fields.length != 0) { var field = fields[0]; - this.populatedFields.push(field); + populatedFields.push(field); var val = data[attrName]; - if(val) { + if(val != null) { if(domClass.contains(field,"map")) { var tableStr = "<table style='border: 1pt'><tr><th style='width: 6em; font-weight: bold'>Header</th><th style='font-weight: bold'>Value</th></tr>"; for(var name in val) { @@ -112,7 +113,7 @@ define(["dojo/_base/xhr", + "/" + encodeURIComponent(showMessage.messageNumber) + "\" target=\"_blank\">Download</a>"; } - this.populatedFields.push(contentField); + populatedFields.push(contentField); registry.byId("showMessage").show(); }; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js index 8dc336b347..8770509c27 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/js/qpid/management/treeView.js @@ -284,6 +284,8 @@ define(["dojo/_base/xhr", controller.show("accesscontrolprovider", details.accesscontrolprovider, {broker: {type:"broker", name:""}}); } else if (details.type == 'plugin') { controller.show("plugin", details.plugin, {broker: {type:"broker", name:""}}); + } else if (details.type == "preferencesprovider") { + controller.show("preferencesprovider", details.preferencesprovider, { type: "authenticationprovider", name: details.authenticationprovider, parent: {broker: {type:"broker", name:""}}}); } }; diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/logs/showLogFileDownloadDialog.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/logs/showLogFileDownloadDialog.html new file mode 100644 index 0000000000..bc633d059a --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/logs/showLogFileDownloadDialog.html @@ -0,0 +1,33 @@ +<!-- + - + - 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. + - + --> +<div> + <div class="contentArea" style="height:320px;overflow:auto"> + <div><b>Select log files to download</b></div> + <div class="logFilesGrid" style='height:300px;width: 580px'></div> + </div> + <div class="dijitDialogPaneActionBar"> + <button value="Download" data-dojo-type="dijit.form.Button" + class="downloadLogsButton" + data-dojo-props="iconClass: 'downloadLogsIcon', label: 'Download' "></button> + <button value="Close" data-dojo-type="dijit.form.Button" data-dojo-props="label: 'Close'" + class="downloadLogsDialogCloseButton"></button> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/logs/showLogViewer.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/logs/showLogViewer.html new file mode 100644 index 0000000000..10ac09a406 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/logs/showLogViewer.html @@ -0,0 +1,29 @@ +<!-- + - + - 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. + - + --> +<div class="logViewer"> + + <div id="broker-logfile"></div> + <br/> + <button data-dojo-type="dijit.form.Button" class="downloadLogs" + data-dojo-props="iconClass: 'downloadLogsIcon', title:'Download Log Files', name: 'downloadLogs'">Download</button> + <br/> +</div> + diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html index 5e876fdc1f..aabaee1e9d 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showAuthProvider.html @@ -27,4 +27,11 @@ <br/> <button data-dojo-type="dijit.form.Button" class="editAuthenticationProviderButton">Edit</button> <button data-dojo-type="dijit.form.Button" class="deleteAuthenticationProviderButton">Delete</button> + <br/> + <br/> + <div class="preferencesPanel" data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Preferences Provider', open: true"> + <div class="preferencesProviderDetails"></div> + <button data-dojo-type="dijit.form.Button" class="addPreferencesProviderButton">Add</button> + </div> + <br/> </div>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html index d9991452af..366ef27c8a 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showBroker.html @@ -190,9 +190,7 @@ <button data-dojo-type="dijit.form.Button" class="deleteAccessControlProvider">Delete Access Control Provider</button> </div> <br/> - <div data-dojo-type="dijit.TitlePane" data-dojo-props="title: 'Log File', open: false"> - <div class="broker-logfile"></div> - </div> - <br/> + <button data-dojo-type="dijit.form.Button" class="logViewer" data-dojo-props="iconClass: 'logViewerIcon'">Log Viewer</button> + <br/><br/> </div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showMessage.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showMessage.html index 0dea508c60..9a6ec55686 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/showMessage.html +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showMessage.html @@ -45,7 +45,7 @@ </tr> <tr style="margin-bottom: 4pt"> <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">Expiration:</span></td> - <td><span class="message-expiration datetime"></span></td> + <td><span class="message-expirationTime datetime"></span></td> </tr> <tr style="margin-bottom: 4pt"> <td style="width: 10em; vertical-align: top"><span style="font-weight: bold;">MIME Type:</span></td> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showPreferences.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showPreferences.html new file mode 100644 index 0000000000..ede111272a --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showPreferences.html @@ -0,0 +1,83 @@ +<!-- + - + - 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. + - + --> +<div data-dojo-type="dijit/Dialog" data-dojo-props="title:'Preferences'" id="preferences.preferencesDialog"> + <div data-dojo-type="dijit/layout/TabContainer" style="width: 600px; height: 400px"> + <div data-dojo-type="dijit/layout/ContentPane" title="Own Preferences" data-dojo-props="selected:true" id="preferences.preferencesTab"> + <form method="post" data-dojo-type="dijit/form/Form" id="preferences.preferencesForm"> + <table cellpadding="0" cellspacing="2" style="overflow: auto; height: 300px;"> + <tr> + <td><strong>Time zone: </strong></td> + <td> + <span id="preferences.timeZone" data-dojo-type="qpid/common/TimeZoneSelector" data-dojo-props="name: 'timeZone'"></span> + </td> + </tr> + <tr> + <td><strong>Update period:</strong></td> + <td><input id="preferences.updatePeriod" name="updatePeriod" data-dojo-type="dijit/form/NumberSpinner" data-dojo-props=" + invalidMessage: 'Invalid value', + required: false, + smallDelta: 1, + value: 5, + constraints: {min:1,max:65535,places:0, pattern: '#####'}, + "/> + </td> + </tr> + <tr> + <td><strong>Save tabs:</strong></td> + <td><input id="preferences.saveTabs" type="checkbox" data-dojo-type="dijit/form/CheckBox" name="saveTabs"/></td> + </tr> + </table> + <div class="dijitDialogPaneActionBar"> + <input type="submit" value="Save Preferences" data-dojo-type="dijit/form/Button" data-dojo-props="label: 'Save Preferences'" id="preferences.saveButton"/> + <button value="Cancel" data-dojo-type="dijit/form/Button" data-dojo-props="label: 'Cancel'" id="preferences.cancelButton"></button> + </div> + </form> + </div> + <div data-dojo-type="dijit/layout/ContentPane" title="Users with Preferences" id="preferences.usersTab"> + <table id="preferences.users" data-dojo-type="dojox/grid/EnhancedGrid" data-dojo-props=" + label:'Trust Stores:', + plugins:{ + indirectSelection: true, + pagination: { + pageSizes: [10, 25, 50, 100], + description: true, + sizeSwitch: true, + pageStepper: true, + gotoButton: true, + maxPageStep: 4, + position: 'bottom' + } + }, + rowSelector:'0px' + " style="height: 300px;"> + <thead> + <tr> + <th field="name" style="width:50%">User</th> + <th field="authenticationProvider" style="width:50%">Authentication Provider</th> + </tr> + </thead> + </table> + <div class="dijitDialogPaneActionBar"> + <button id="preferences.deletePreeferencesButton" data-dojo-type="dijit/form/Button" data-dojo-props="label:'Delete Preferences', title:'Delete preferences for selected users'">Delete Preferences</button> + </div> + </div> + </div> +</div> diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/showPreferencesProvider.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/showPreferencesProvider.html new file mode 100644 index 0000000000..a1885acddf --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/main/java/resources/showPreferencesProvider.html @@ -0,0 +1,40 @@ +<!-- + - + - 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. + - + --> + <div class="preferencesProvider"> + <div class="preferencesProviderAttributes"> + <div style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 100px;">Type:</div> + <div class="preferencesProviderType" style="float:left;"></div> + </div> + <div style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 100px;">Name:</div> + <div class="preferencesProviderName" style="float:left;"></div> + </div> + <div style="clear:both"> + <div class="formLabel-labelCell" style="float:left; width: 100px;">State:</div> + <div class="preferencesProviderState" style="float:left;"></div> + </div> + <div class="preferencesDetails"></div> + </div> + <br/> + <button data-dojo-type="dijit.form.Button" class="deletePreferencesProviderButton">Delete</button> + <button data-dojo-type="dijit.form.Button" class="editPreferencesProviderButton">Edit</button> +</div>
\ No newline at end of file diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/store/memory/add.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/store/memory/add.html deleted file mode 100644 index e69de29bb2..0000000000 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/store/memory/add.html +++ /dev/null diff --git a/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/store/pool/none/add.html b/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/store/pool/none/add.html deleted file mode 100644 index e69de29bb2..0000000000 --- a/qpid/java/broker-plugins/management-http/src/main/java/resources/virtualhost/store/pool/none/add.html +++ /dev/null diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java index 18d774e341..d0a357fd28 100644 --- a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/HttpManagementTest.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; +import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.test.utils.QpidTestCase; @@ -88,13 +89,13 @@ public class HttpManagementTest extends QpidTestCase _management.isHttpBasicAuthenticationEnabled()); } - public void testGetSubjectCreator() + public void testGetAuthenticationProvider() { SocketAddress localAddress = InetSocketAddress.createUnresolved("localhost", 8080); - SubjectCreator subjectCreator = mock(SubjectCreator.class); - when(_broker.getSubjectCreator(localAddress)).thenReturn(subjectCreator); - SubjectCreator httpManagementSubjectCreator = _management.getSubjectCreator(localAddress); - assertEquals("Unexpected subject creator", subjectCreator, httpManagementSubjectCreator); + AuthenticationProvider brokerAuthenticationProvider = mock(AuthenticationProvider.class); + when(_broker.getAuthenticationProvider(localAddress)).thenReturn(brokerAuthenticationProvider); + AuthenticationProvider authenticationProvider = _management.getAuthenticationProvider(localAddress); + assertEquals("Unexpected subject creator", brokerAuthenticationProvider, authenticationProvider); } } diff --git a/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/log/LogFileHelperTest.java b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/log/LogFileHelperTest.java new file mode 100644 index 0000000000..608ef28f02 --- /dev/null +++ b/qpid/java/broker-plugins/management-http/src/test/java/org/apache/qpid/server/management/plugin/log/LogFileHelperTest.java @@ -0,0 +1,339 @@ +/* + * + * 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.qpid.server.management.plugin.log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.log4j.Appender; +import org.apache.log4j.DailyRollingFileAppender; +import org.apache.log4j.FileAppender; +import org.apache.log4j.QpidCompositeRollingAppender; +import org.apache.log4j.RollingFileAppender; +import org.apache.log4j.varia.ExternallyRolledFileAppender; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.util.FileUtils; + +public class LogFileHelperTest extends QpidTestCase +{ + private Map<String, List<File>> _appendersFiles; + private File _compositeRollingAppenderBackupFolder; + private List<Appender> _appenders; + private LogFileHelper _helper; + + public void setUp() throws Exception + { + super.setUp(); + _appendersFiles = new HashMap<String, List<File>>(); + _compositeRollingAppenderBackupFolder = new File(TMP_FOLDER, "_compositeRollingAppenderBackupFolder"); + _compositeRollingAppenderBackupFolder.mkdirs(); + + _appendersFiles.put(FileAppender.class.getSimpleName(), + Collections.singletonList(TestFileUtils.createTempFile(this, ".log", "FileAppender"))); + _appendersFiles.put(DailyRollingFileAppender.class.getSimpleName(), + Collections.singletonList(TestFileUtils.createTempFile(this, ".log", "DailyRollingFileAppender"))); + _appendersFiles.put(RollingFileAppender.class.getSimpleName(), + Collections.singletonList(TestFileUtils.createTempFile(this, ".log", "RollingFileAppender"))); + _appendersFiles.put(ExternallyRolledFileAppender.class.getSimpleName(), + Collections.singletonList(TestFileUtils.createTempFile(this, ".log", "ExternallyRolledFileAppender"))); + + File file = TestFileUtils.createTempFile(this, ".log", "QpidCompositeRollingAppender"); + File backUpFile = File.createTempFile(file.getName() + ".", ".1." + LogFileHelper.GZIP_EXTENSION); + _appendersFiles.put(QpidCompositeRollingAppender.class.getSimpleName(), Arrays.asList(file, backUpFile)); + + FileAppender fileAppender = new FileAppender(); + DailyRollingFileAppender dailyRollingFileAppender = new DailyRollingFileAppender(); + RollingFileAppender rollingFileAppender = new RollingFileAppender(); + ExternallyRolledFileAppender externallyRolledFileAppender = new ExternallyRolledFileAppender(); + QpidCompositeRollingAppender qpidCompositeRollingAppender = new QpidCompositeRollingAppender(); + qpidCompositeRollingAppender.setbackupFilesToPath(_compositeRollingAppenderBackupFolder.getPath()); + + _appenders = new ArrayList<Appender>(); + _appenders.add(fileAppender); + _appenders.add(dailyRollingFileAppender); + _appenders.add(rollingFileAppender); + _appenders.add(externallyRolledFileAppender); + _appenders.add(qpidCompositeRollingAppender); + + for (Appender appender : _appenders) + { + FileAppender fa = (FileAppender) appender; + fa.setName(fa.getClass().getSimpleName()); + fa.setFile(_appendersFiles.get(fa.getClass().getSimpleName()).get(0).getPath()); + } + + _helper = new LogFileHelper(_appenders); + } + + public void tearDown() throws Exception + { + try + { + for (List<File> files : _appendersFiles.values()) + { + for (File file : files) + { + try + { + FileUtils.delete(file, false); + } + catch (Exception e) + { + // ignore + } + } + } + FileUtils.delete(_compositeRollingAppenderBackupFolder, true); + } + finally + { + super.tearDown(); + } + } + + public void testGetLogFileDetailsWithLocations() throws Exception + { + List<LogFileDetails> details = _helper.getLogFileDetails(true); + + assertLogFiles(details, true); + } + + public void testGetLogFileDetailsWithoutLocations() throws Exception + { + List<LogFileDetails> details = _helper.getLogFileDetails(false); + + assertLogFiles(details, false); + } + + public void testWriteLogFilesForAllLogs() throws Exception + { + List<LogFileDetails> details = _helper.getLogFileDetails(true); + File f = TestFileUtils.createTempFile(this, ".zip"); + + FileOutputStream os = new FileOutputStream(f); + try + { + _helper.writeLogFiles(details, os); + } + finally + { + if (os != null) + { + os.close(); + } + } + + assertWrittenFile(f, details); + } + + public void testWriteLogFile() throws Exception + { + File file = _appendersFiles.get(FileAppender.class.getSimpleName()).get(0); + + File f = TestFileUtils.createTempFile(this, ".log"); + FileOutputStream os = new FileOutputStream(f); + try + { + _helper.writeLogFile(file, os); + } + finally + { + if (os != null) + { + os.close(); + } + } + + assertEquals("Unexpected log content", FileAppender.class.getSimpleName(), FileUtils.readFileAsString(f)); + } + + public void testFindLogFileDetails() + { + String[] logFileDisplayedPaths = new String[6]; + File[] files = new File[logFileDisplayedPaths.length]; + int i = 0; + for (Map.Entry<String, List<File>> entry : _appendersFiles.entrySet()) + { + String appenderName = entry.getKey(); + List<File> appenderFiles = entry.getValue(); + for (File logFile : appenderFiles) + { + logFileDisplayedPaths[i] = appenderName + "/" + logFile.getName(); + files[i++] = logFile; + } + } + + List<LogFileDetails> logFileDetails = _helper.findLogFileDetails(logFileDisplayedPaths); + assertEquals("Unexpected details size", logFileDisplayedPaths.length, logFileDetails.size()); + + boolean gzipFileFound = false; + for (int j = 0; j < logFileDisplayedPaths.length; j++) + { + String displayedPath = logFileDisplayedPaths[j]; + String[] parts = displayedPath.split("/"); + LogFileDetails d = logFileDetails.get(j); + assertEquals("Unexpected name", parts[1], d.getName()); + assertEquals("Unexpected appender", parts[0], d.getAppenderName()); + if (files[j].getName().endsWith(LogFileHelper.GZIP_EXTENSION)) + { + assertEquals("Unexpected mime type for gz file", LogFileHelper.GZIP_MIME_TYPE, d.getMimeType()); + gzipFileFound = true; + } + else + { + assertEquals("Unexpected mime type", LogFileHelper.TEXT_MIME_TYPE, d.getMimeType()); + } + assertEquals("Unexpecte file location", files[j], d.getLocation()); + assertEquals("Unexpecte file size", files[j].length(), d.getSize()); + assertEquals("Unexpecte file last modified date", files[j].lastModified(), d.getLastModified()); + } + assertTrue("Gzip log file is not found", gzipFileFound); + } + + public void testFindLogFileDetailsForNotExistingAppender() + { + String[] logFileDisplayedPaths = { "NotExistingAppender/qpid.log" }; + List<LogFileDetails> details = _helper.findLogFileDetails(logFileDisplayedPaths); + assertTrue("No details should be created for non-existing appender", details.isEmpty()); + } + + public void testFindLogFileDetailsForNotExistingFile() + { + String[] logFileDisplayedPaths = { "FileAppender/qpid-non-existing.log" }; + List<LogFileDetails> details = _helper.findLogFileDetails(logFileDisplayedPaths); + assertTrue("No details should be created for non-existing file", details.isEmpty()); + } + + public void testFindLogFileDetailsForIncorectlySpecifiedLogFilePath() + { + String[] logFileDisplayedPaths = { "FileAppender\\" + _appendersFiles.get("FileAppender").get(0).getName() }; + try + { + _helper.findLogFileDetails(logFileDisplayedPaths); + fail("Exception is expected for incorectly set path to log file"); + } + catch (IllegalArgumentException e) + { + // pass + } + } + + private void assertLogFiles(List<LogFileDetails> details, boolean includeLocation) + { + for (Map.Entry<String, List<File>> appenderData : _appendersFiles.entrySet()) + { + String appenderName = (String) appenderData.getKey(); + List<File> files = appenderData.getValue(); + + for (File logFile : files) + { + String logFileName = logFile.getName(); + LogFileDetails d = findLogFileDetails(logFileName, appenderName, details); + assertNotNull("Log file " + logFileName + " is not found for appender " + appenderName, d); + if (includeLocation) + { + assertEquals("Log file " + logFileName + " is different in appender " + appenderName, d.getLocation(), + logFile); + } + } + } + } + + private LogFileDetails findLogFileDetails(String logFileName, String appenderName, List<LogFileDetails> logFileDetails) + { + LogFileDetails d = null; + for (LogFileDetails lfd : logFileDetails) + { + if (lfd.getName().equals(logFileName) && lfd.getAppenderName().equals(appenderName)) + { + d = lfd; + break; + } + } + return d; + } + + private void assertWrittenFile(File f, List<LogFileDetails> details) throws FileNotFoundException, IOException + { + FileInputStream fis = new FileInputStream(f); + try + { + ZipInputStream zis = new ZipInputStream(fis); + ZipEntry ze = zis.getNextEntry(); + + while (ze != null) + { + String entryName = ze.getName(); + String[] parts = entryName.split("/"); + + String appenderName = parts[0]; + String logFileName = parts[1]; + + LogFileDetails d = findLogFileDetails(logFileName, appenderName, details); + + assertNotNull("Unexpected entry " + entryName, d); + details.remove(d); + + File logFile = d.getLocation(); + String logContent = FileUtils.readFileAsString(logFile); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + while ((len = zis.read(buffer)) > 0) + { + baos.write(buffer, 0, len); + } + baos.close(); + + assertEquals("Unexpected log file content", logContent, baos.toString()); + + ze = zis.getNextEntry(); + } + + zis.closeEntry(); + zis.close(); + + } + finally + { + if (fis != null) + { + fis.close(); + } + } + assertEquals("Not all log files have been output", 0, details.size()); + } + +} |
