1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. 22 */ 23 package org.opengrok.indexer.web.messages; 24 25 import com.fasterxml.jackson.core.JsonProcessingException; 26 import com.fasterxml.jackson.databind.ObjectMapper; 27 import org.opengrok.indexer.configuration.Group; 28 import org.opengrok.indexer.configuration.Project; 29 import org.opengrok.indexer.configuration.RuntimeEnvironment; 30 import org.opengrok.indexer.logger.LoggerFactory; 31 import org.opengrok.indexer.web.Util; 32 33 import java.io.IOException; 34 import java.io.Writer; 35 import java.sql.Date; 36 import java.text.SimpleDateFormat; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Set; 43 import java.util.SortedSet; 44 import java.util.logging.Level; 45 import java.util.logging.Logger; 46 import java.util.stream.Collectors; 47 48 public final class MessagesUtils { 49 50 private static final Logger LOGGER = LoggerFactory.getLogger(MessagesUtils.class); 51 MessagesUtils()52 private MessagesUtils() { 53 // private to ensure static 54 } 55 56 static final class TaggedMessagesContainer implements JSONable { 57 58 private final String tag; 59 private final SortedSet<MessagesContainer.AcceptedMessage> messages; 60 TaggedMessagesContainer(String tag, SortedSet<MessagesContainer.AcceptedMessage> messages)61 TaggedMessagesContainer(String tag, SortedSet<MessagesContainer.AcceptedMessage> messages) { 62 this.tag = tag; 63 this.messages = messages; 64 } 65 getTag()66 public String getTag() { 67 return tag; 68 } 69 getMessages()70 public SortedSet<MessagesContainer.AcceptedMessage> getMessages() { 71 return messages; 72 } 73 } 74 75 /** 76 * Print list of messages into output. 77 * 78 * @param out output 79 * @param set set of messages 80 */ printMessages(Writer out, SortedSet<MessagesContainer.AcceptedMessage> set)81 public static void printMessages(Writer out, SortedSet<MessagesContainer.AcceptedMessage> set) { 82 printMessages(out, set, false); 83 } 84 85 /** 86 * Print set of messages into output. 87 * @param out output 88 * @param set set of messages 89 * @param limited if the container should be limited 90 */ printMessages(Writer out, SortedSet<MessagesContainer.AcceptedMessage> set, boolean limited)91 private static void printMessages(Writer out, SortedSet<MessagesContainer.AcceptedMessage> set, boolean limited) { 92 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); 93 if (!set.isEmpty()) { 94 try { 95 out.write("<ul class=\"message-group"); 96 if (limited) { 97 out.write(" limited"); 98 } 99 out.write("\">\n"); 100 for (MessagesContainer.AcceptedMessage m : set) { 101 out.write("<li class=\"message-group-item "); 102 out.write(Util.encode(m.getMessage().getMessageLevel().toString())); 103 out.write("\" title=\"Expires on "); 104 out.write(Util.encode(df.format(Date.from(m.getExpirationTime())))); 105 out.write("\">"); 106 out.write(Util.encode(df.format(Date.from(m.getAcceptedTime())))); 107 out.write(": "); 108 out.write(m.getMessage().getText()); 109 out.write("</li>"); 110 } 111 out.write("</ul>"); 112 } catch (IOException ex) { 113 LOGGER.log(Level.WARNING, 114 "An error occurred for a group of messages", ex); 115 } 116 } 117 } 118 119 /** 120 * Print set of tagged messages into JSON object. 121 * 122 * @return JSON string or empty string 123 */ taggedMessagesToJson(Set<TaggedMessagesContainer> messages)124 private static String taggedMessagesToJson(Set<TaggedMessagesContainer> messages) { 125 if (messages.isEmpty()) { 126 return JSONable.EMPTY; 127 } 128 129 ObjectMapper mapper = new ObjectMapper(); 130 131 try { 132 return mapper.writeValueAsString(messages); 133 } catch (JsonProcessingException e) { 134 LOGGER.log(Level.WARNING, String.format("failed to encode '%s' to JSON: ", messages), e); 135 return JSONable.EMPTY; 136 } 137 } 138 139 /** 140 * Print messages for given tags into JSON array. 141 * 142 * @param tags list of tags 143 * @return JSON array of the messages (the same as the parameter) 144 */ messagesToJson(String... tags)145 public static String messagesToJson(String... tags) { 146 Set<TaggedMessagesContainer> messages = new HashSet<>(); 147 148 for (String tag : tags) { 149 SortedSet<MessagesContainer.AcceptedMessage> messagesWithTag = RuntimeEnvironment.getInstance().getMessages(tag); 150 if (messagesWithTag.isEmpty()) { 151 continue; 152 } 153 154 TaggedMessagesContainer container = new TaggedMessagesContainer(tag, messagesWithTag); 155 messages.add(container); 156 } 157 158 return taggedMessagesToJson(messages); 159 } 160 161 /** 162 * Print messages for given tags into JSON array. 163 * 164 * @param tags list of tags 165 * @return json array of the messages 166 * @see #messagesToJson(String...) 167 */ messagesToJson(List<String> tags)168 private static String messagesToJson(List<String> tags) { 169 return messagesToJson(tags.toArray(new String[0])); 170 } 171 172 /** 173 * Print messages for given project into JSON. These messages are 174 * tagged by project description or tagged by any of the project's group name. 175 * 176 * @param project the project 177 * @param additionalTags additional list of tags 178 * @return JSON string 179 * @see #messagesToJson(String...) 180 */ messagesToJson(Project project, String... additionalTags)181 public static String messagesToJson(Project project, String... additionalTags) { 182 if (project == null) { 183 return JSONable.EMPTY; 184 } 185 186 List<String> tags = new ArrayList<>(Arrays.asList(additionalTags)); 187 tags.add(project.getName()); 188 project.getGroups().stream().map(Group::getName).forEach(tags::add); 189 190 return messagesToJson(tags); 191 } 192 193 /** 194 * Print messages for given project into JSON array. These messages are 195 * tagged by project description or tagged by any of the project's group 196 * name. 197 * 198 * @param project the project 199 * @return the json array 200 * @see #messagesToJson(Project, String...) 201 */ messagesToJson(Project project)202 public static String messagesToJson(Project project) { 203 return messagesToJson(project, new String[0]); 204 } 205 206 /** 207 * Print messages for given group into JSON. 208 * 209 * @param group the group 210 * @param additionalTags additional list of tags 211 * @return JSON string 212 * @see #messagesToJson(java.util.List) 213 */ messagesToJson(Group group, String... additionalTags)214 private static String messagesToJson(Group group, String... additionalTags) { 215 List<String> tags = new ArrayList<>(); 216 217 tags.add(group.getName()); 218 tags.addAll(Arrays.asList(additionalTags)); 219 220 return messagesToJson(tags); 221 } 222 223 /** 224 * Convert messages for given group into JSON. 225 * 226 * @param group the group 227 * @return JSON string 228 * @see #messagesToJson(Group, String...) 229 */ messagesToJson(Group group)230 public static String messagesToJson(Group group) { 231 return messagesToJson(group, new String[0]); 232 } 233 234 /** 235 * @return name of highest cssClass of messages present in the system or null. 236 */ getHighestMessageLevel(Collection<MessagesContainer.AcceptedMessage> messages)237 static String getHighestMessageLevel(Collection<MessagesContainer.AcceptedMessage> messages) { 238 return messages. 239 stream(). 240 map(MessagesContainer.AcceptedMessage::getMessageLevel). 241 max(Message.MessageLevel.VALUE_COMPARATOR). 242 map(Message.MessageLevel::toString). 243 orElse(null); 244 } 245 246 /** 247 * @param tags message tags 248 * @return name of highest cssClass of messages present in the system or null. 249 */ getMessageLevel(String... tags)250 public static String getMessageLevel(String... tags) { 251 Set<MessagesContainer.AcceptedMessage> messages; 252 RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 253 254 messages = Arrays.stream(tags). 255 map(env::getMessages). 256 flatMap(Collection::stream). 257 collect(Collectors.toSet()); 258 259 return getHighestMessageLevel(messages); 260 } 261 } 262