001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/tool/tags/sakai_2-4-1/tool-impl/impl/src/java/org/sakaiproject/tool/impl/ToolComponent.java $
003: * $Id: ToolComponent.java 16867 2006-10-08 17:58:50Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.impl;
021:
022: import java.io.File;
023: import java.io.InputStream;
024: import java.util.Arrays;
025: import java.util.Collection;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.Map;
029: import java.util.Properties;
030: import java.util.Set;
031: import java.util.Vector;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.sakaiproject.thread_local.api.ThreadLocalManager;
036: import org.sakaiproject.tool.api.Placement;
037: import org.sakaiproject.tool.api.Tool;
038: import org.sakaiproject.tool.api.ToolManager;
039: import org.sakaiproject.util.StringUtil;
040: import org.sakaiproject.util.Xml;
041: import org.w3c.dom.Document;
042: import org.w3c.dom.Element;
043: import org.w3c.dom.Node;
044: import org.w3c.dom.NodeList;
045:
046: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
047:
048: /**
049: * <p>
050: * ToolComponent is the standard implementation of the Sakai Tool API.
051: * </p>
052: */
053: public abstract class ToolComponent implements ToolManager {
054: /** Our log (commons). */
055: private static Log M_log = LogFactory.getLog(ToolComponent.class);
056:
057: /** Key in the ThreadLocalManager for binding our current placement. */
058: protected final static String CURRENT_PLACEMENT = "sakai:ToolComponent:current.placement";
059:
060: /** Key in the ThreadLocalManager for binding our current tool. */
061: protected final static String CURRENT_TOOL = "sakai:ToolComponent:current.tool";
062:
063: /** The registered tools. */
064: protected Map m_tools = new ConcurrentReaderHashMap();
065:
066: /** tool ids to be hidden - their catagories don't matter, they don't show up on any catagorized listing. */
067: protected String[] m_toolIdsToHide = null;
068:
069: /**********************************************************************************************************************************************************************************************************************************************************
070: * Dependencies
071: *********************************************************************************************************************************************************************************************************************************************************/
072:
073: /**
074: * @return the ThreadLocalManager collaborator.
075: */
076: protected abstract ThreadLocalManager threadLocalManager();
077:
078: /**********************************************************************************************************************************************************************************************************************************************************
079: * Configuration
080: *********************************************************************************************************************************************************************************************************************************************************/
081:
082: /** tool ids to be stealthed (hidden). */
083: protected Collection m_stealthToolIds = null;
084:
085: /**
086: * Configuration - set the list of tool ids to be "stealthed". A stealthed tool does not show up in a category list of tools.
087: *
088: * @param toolIds
089: * The comma-separated list of tool ids to be stealthed.
090: */
091: public void setStealthTools(String toolIds) {
092: if ((toolIds == null) || (toolIds.length() == 0)) {
093: m_stealthToolIds = null;
094: } else {
095: m_stealthToolIds = new Vector();
096: String[] items = StringUtil.split(toolIds, ",");
097: for (int i = 0; i < items.length; i++) {
098: m_stealthToolIds.add(items[i]);
099: }
100: }
101: }
102:
103: /** tool ids to be visible, not hidden, even if marked hidden or stealthed. */
104: protected Collection m_visibleToolIds = null;
105:
106: /**
107: * Configuration - set the list of tool ids to be visible, not hidden, even if marked hidden or stealthed.
108: *
109: * @param toolIds
110: * The comma-separated list of tool ids to be visible.
111: */
112: public void setVisibleTools(String toolIds) {
113: if ((toolIds == null) || (toolIds.length() == 0)) {
114: m_visibleToolIds = null;
115: } else {
116: m_visibleToolIds = new Vector();
117: String[] items = StringUtil.split(toolIds, ",");
118: for (int i = 0; i < items.length; i++) {
119: m_visibleToolIds.add(items[i]);
120: }
121: }
122: }
123:
124: /** tool ids to be hidden, adding to the stealth list (but not trumping the visible list) */
125: protected Collection m_hiddenToolIds = null;
126:
127: /**
128: * Configuration - set the list of tool ids to be visible, not hidden, even if marked hidden or stealthed.
129: *
130: * @param toolIds
131: * The comma-separated list of tool ids to be visible.
132: */
133: public void setHiddenTools(String toolIds) {
134: if ((toolIds == null) || (toolIds.length() == 0)) {
135: m_hiddenToolIds = null;
136: } else {
137: m_hiddenToolIds = new Vector();
138: String[] items = StringUtil.split(toolIds, ",");
139: for (int i = 0; i < items.length; i++) {
140: m_hiddenToolIds.add(items[i]);
141: }
142: }
143: }
144:
145: /**********************************************************************************************************************************************************************************************************************************************************
146: * Init and Destroy
147: *********************************************************************************************************************************************************************************************************************************************************/
148:
149: /**
150: * Final initialization, once all dependencies are set.
151: */
152: public void init() {
153: // compute the tools to hide: these are the stealth tools plus the hidden tools, minus the visible ones
154: Collection toHide = new HashSet();
155:
156: if (m_stealthToolIds != null) {
157: toHide.addAll(m_stealthToolIds);
158: }
159:
160: if (m_hiddenToolIds != null) {
161: toHide.addAll(m_hiddenToolIds);
162: }
163:
164: if (m_visibleToolIds != null) {
165: toHide.removeAll(m_visibleToolIds);
166: }
167:
168: // collect the hiddens for logging
169: StringBuffer hidden = new StringBuffer();
170:
171: if (!toHide.isEmpty()) {
172: m_toolIdsToHide = new String[toHide.size()];
173: int pos = 0;
174: for (Iterator i = toHide.iterator(); i.hasNext();) {
175: m_toolIdsToHide[pos] = (String) i.next();
176:
177: hidden.append(m_toolIdsToHide[pos]);
178: hidden.append(" ");
179:
180: pos++;
181: }
182: Arrays.sort(m_toolIdsToHide);
183: }
184:
185: M_log.info("init(): hidden tools: " + hidden.toString());
186: }
187:
188: /**
189: * Final cleanup.
190: */
191: public void destroy() {
192: M_log.info("destroy()");
193: }
194:
195: /**********************************************************************************************************************************************************************************************************************************************************
196: * Work interface
197: *********************************************************************************************************************************************************************************************************************************************************/
198:
199: /**
200: * @inheritDoc
201: */
202: public Set findTools(Set categories, Set keywords) {
203: Set rv = new HashSet();
204:
205: for (Iterator i = m_tools.values().iterator(); i.hasNext();) {
206: Tool tool = (Tool) i.next();
207: if (matchCriteria(categories, tool.getCategories())
208: && matchCriteria(keywords, tool.getKeywords())) {
209: // add if not hidden (requests for no (null) category include all, even hidden items)
210: if ((categories == null)
211: || (m_toolIdsToHide == null)
212: || (Arrays.binarySearch(m_toolIdsToHide, tool
213: .getId()) < 0)) {
214: rv.add(tool);
215: }
216: }
217: }
218:
219: return rv;
220: }
221:
222: /**
223: * @inheritDoc
224: */
225: public Placement getCurrentPlacement() {
226: return (Placement) threadLocalManager().get(CURRENT_PLACEMENT);
227: }
228:
229: /**
230: * @inheritDoc
231: */
232: public Tool getCurrentTool() {
233: return (Tool) threadLocalManager().get(CURRENT_TOOL);
234: }
235:
236: /**
237: * @inheritDoc
238: */
239: public Tool getTool(String id) {
240: return (Tool) m_tools.get(id);
241: }
242:
243: /**
244: * Check the target values for a match in the criteria. If criteria is empty or null, the target is a match.
245: *
246: * @param criteria
247: * The set of String values that is the criteria - any one in the target is a match
248: * @param target
249: * The set of String values to check against the criteria.
250: * @return true if the target meets the criteria, false if not.
251: */
252: protected boolean matchCriteria(Set criteria, Set target) {
253: if ((criteria == null) || (criteria.isEmpty()))
254: return true;
255:
256: for (Iterator i = target.iterator(); i.hasNext();) {
257: String t = (String) i.next();
258: if (criteria.contains(t))
259: return true;
260: }
261: return false;
262: }
263:
264: /**
265: * @inheritDoc
266: */
267: public void register(Document toolXml) {
268: Element root = toolXml.getDocumentElement();
269: if (!root.getTagName().equals("registration")) {
270: M_log
271: .info("register: invalid root element (expecting \"registration\"): "
272: + root.getTagName());
273: return;
274: }
275:
276: // read the children nodes (tools)
277: NodeList rootNodes = root.getChildNodes();
278: final int rootNodesLength = rootNodes.getLength();
279: for (int i = 0; i < rootNodesLength; i++) {
280: Node rootNode = rootNodes.item(i);
281: if (rootNode.getNodeType() != Node.ELEMENT_NODE)
282: continue;
283: Element rootElement = (Element) rootNode;
284:
285: // for tool
286: if (rootElement.getTagName().equals("tool")) {
287: org.sakaiproject.util.Tool tool = new org.sakaiproject.util.Tool();
288:
289: tool.setId(rootElement.getAttribute("id").trim());
290: tool.setTitle(rootElement.getAttribute("title").trim());
291: tool.setDescription(rootElement.getAttribute(
292: "description").trim());
293: tool.setHome(StringUtil.trimToNull(rootElement
294: .getAttribute("home")));
295:
296: if ("tool".equals(rootElement
297: .getAttribute("accessSecurity"))) {
298: tool.setAccessSecurity(Tool.AccessSecurity.TOOL);
299: } else {
300: tool.setAccessSecurity(Tool.AccessSecurity.PORTAL);
301: }
302:
303: // collect values for these collections
304: Properties finalConfig = new Properties();
305: Properties mutableConfig = new Properties();
306: Set categories = new HashSet();
307: Set keywords = new HashSet();
308:
309: NodeList kids = rootElement.getChildNodes();
310: final int kidsLength = kids.getLength();
311: for (int k = 0; k < kidsLength; k++) {
312: Node kidNode = kids.item(k);
313: if (kidNode.getNodeType() != Node.ELEMENT_NODE)
314: continue;
315: Element kidElement = (Element) kidNode;
316:
317: // for configuration
318: if (kidElement.getTagName().equals("configuration")) {
319: String name = kidElement.getAttribute("name")
320: .trim();
321: String value = kidElement.getAttribute("value")
322: .trim();
323: String type = kidElement.getAttribute("type")
324: .trim();
325: if (name.length() > 0) {
326: if ("final".equals(type)) {
327: finalConfig.put(name, value);
328: } else {
329: mutableConfig.put(name, value);
330: }
331: }
332: }
333:
334: // for category
335: if (kidElement.getTagName().equals("category")) {
336: String name = kidElement.getAttribute("name")
337: .trim();
338: if (name.length() > 0) {
339: categories.add(name);
340: }
341: }
342:
343: // for keyword
344: if (kidElement.getTagName().equals("keyword")) {
345: String name = kidElement.getAttribute("name")
346: .trim();
347: if (name.length() > 0) {
348: keywords.add(name);
349: }
350: }
351: }
352:
353: // set the tool's collected values
354: tool.setRegisteredConfig(finalConfig, mutableConfig);
355: tool.setCategories(categories);
356: tool.setKeywords(keywords);
357:
358: register(tool);
359: }
360: }
361: }
362:
363: /**
364: * @inheritDoc
365: */
366: public void register(File toolXmlFile) {
367: String path = toolXmlFile.getAbsolutePath();
368: if (!path.endsWith(".xml")) {
369: M_log.info("register: skiping non .xml file: " + path);
370: return;
371: }
372:
373: M_log.info("register: file: " + path);
374:
375: Document doc = Xml.readDocument(path);
376: register(doc);
377: }
378:
379: /**
380: * @inheritDoc
381: */
382: public void register(InputStream toolXmlStream) {
383: Document doc = Xml.readDocumentFromStream(toolXmlStream);
384: try {
385: toolXmlStream.close();
386: } catch (Exception e) {
387: }
388:
389: register(doc);
390: }
391:
392: /**
393: * @inheritDoc
394: */
395: public void register(Tool tool) {
396: m_tools.put(tool.getId(), tool);
397: }
398:
399: /**
400: * Establish the Tool associated with the current request / thread
401: *
402: * @param Tool
403: * The current Tool, or null if there is none.
404: */
405: protected void setCurrentPlacement(Placement placement) {
406: threadLocalManager().set(CURRENT_PLACEMENT, placement);
407: }
408:
409: /**
410: * Establish the Tool associated with the current request / thread
411: *
412: * @param Tool
413: * The current Tool, or null if there is none.
414: */
415: protected void setCurrentTool(Tool tool) {
416: threadLocalManager().set(CURRENT_TOOL, tool);
417: }
418: }
|