source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/Action.java@ 28382

Last change on this file since 28382 was 28382, checked in by davidb, 11 years ago

Elimination of the 'this.doc' field from the Action baseclass and the subclasses that rely on it. For Greenstone3 purposes it is unsafe to create this object in the constructor to the action and then store it for other methods to access. This is because the Greenstone 3 (and in particular calls to 'process' operate in a multi-threaded context, that is managed by the Servlet server (e.g. Tomcat by default). Calls to DOM methods are not guaranteed to be thread safe, this became apparent when we started looking in to an exception that was being thrown, and centred around use of the DOM method 'item(i)'. The change this commit makes is to remove 'this.doc' being stored as a field. A document is now created in the top level of a call to 'process()' and when a DOM reference is needed in a subsequent method an Element variable (typically passed in as a parameter to the method) is used (through 'Document doc = element.getOwnerDocument()') to gain access to the DOM

  • Property svn:keywords set to Author Date Id Revision
File size: 9.6 KB
Line 
1package org.greenstone.gsdl3.action;
2
3import java.util.HashMap;
4import java.util.HashSet;
5import java.util.Iterator;
6
7import org.apache.log4j.Logger;
8import org.greenstone.gsdl3.core.ModuleInterface;
9import org.greenstone.gsdl3.util.GSConstants;
10import org.greenstone.gsdl3.util.GSParams;
11import org.greenstone.gsdl3.util.GSXML;
12import org.greenstone.gsdl3.util.GSXSLT;
13import org.greenstone.gsdl3.util.UserContext;
14import org.greenstone.gsdl3.util.XMLConverter;
15import org.w3c.dom.Document;
16import org.w3c.dom.Element;
17import org.w3c.dom.Node;
18import org.w3c.dom.NodeList;
19
20/** base class for Actions */
21abstract public class Action
22{
23
24 /** the system set up variables */
25 protected HashMap<String, Object> config_params = null;
26
27
28 /** a converter class to parse XML and create Docs */
29 protected XMLConverter converter = null;
30 /**
31 * a reference to the message router that it must talk to to get info. it
32 * may be a communicator acting as a proxy, but it doesn't care about that
33 */
34 protected ModuleInterface mr = null;
35
36 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.action.Action.class.getName());
37
38 public Action()
39 {
40 this.converter = new XMLConverter();
41 }
42
43 /** the config variables must be set before configure is called */
44 public void setConfigParams(HashMap<String, Object> params)
45 {
46 this.config_params = params;
47 }
48
49 /** sets the message router */
50 public void setMessageRouter(ModuleInterface m)
51 {
52 this.mr = m;
53 }
54
55 public boolean configure()
56 {
57 // does nothing yet
58 return true;
59 }
60
61 /**
62 * process takes an xml representation of cgi args and returns the page of
63 * results - may be in html/xml/other depending on the output att of the
64 * request
65 */
66 public String process(String xml_in)
67 {
68
69 Document message_doc = this.converter.getDOM(xml_in);
70 if (message_doc == null)
71 {
72 logger.error("Couldn't parse request");
73 logger.error(xml_in);
74 return null;
75 }
76 Node result = process(message_doc);
77 return this.converter.getString(result);
78 }
79
80 /** the main process method - must be implemented in subclass */
81 abstract public Node process(Node xml_in);
82
83 /**
84 * tell the param class what its arguments are if an action has its own
85 * arguments, this should add them to the params object - particularly
86 * important for args that should not be saved
87 */
88 public boolean addActionParameters(GSParams params)
89 {
90 return true;
91 }
92
93 protected void getRequiredMetadataNames(Element format, HashSet<String> meta_names)
94 {
95 extractMetadataNames(format, meta_names);
96 addLinkMetadataNames(format, meta_names);
97 }
98
99 // should change to metadataList?? and use attributes for select rather than
100 // prepending parent_ etc
101 protected void extractMetadataNames(Element format, HashSet<String> meta_names)
102 {
103 NodeList metadata_nodes = format.getElementsByTagNameNS(GSXML.GSF_NAMESPACE, "metadata"); // gsf:metadata
104 for (int i = 0; i < metadata_nodes.getLength(); i++)
105 {
106 Element elem = (Element) metadata_nodes.item(i);
107 String name = elem.getAttribute("name");
108 String select = elem.getAttribute("select");
109
110 if (!select.equals(""))
111 {
112 name = select + GSConstants.META_RELATION_SEP + name;
113 }
114 meta_names.add(name);
115 }
116
117 NodeList foreach_metadata_nodes = format.getElementsByTagNameNS(GSXML.GSF_NAMESPACE, "foreach-metadata"); // gsf:foreach-metadata
118 for (int i = 0; i < foreach_metadata_nodes.getLength(); i++)
119 {
120 Element elem = (Element) foreach_metadata_nodes.item(i);
121 String name = elem.getAttribute("name");
122 String select = elem.getAttribute("select");
123
124 if (!select.equals(""))
125 {
126 name = select + GSConstants.META_RELATION_SEP + name;
127 }
128 meta_names.add(name);
129 }
130 }
131
132 protected void addLinkMetadataNames(Element format, HashSet<String> meta_names)
133 {
134 // The XSL tranform for
135 // gsf:link type="source"
136 // makes use of 'assocfilepath' so need to make sure it's asked for
137
138 boolean getEquivLinkMeta = false;
139
140 NodeList link_nodes = format.getElementsByTagNameNS(GSXML.GSF_NAMESPACE, "link");
141 for (int i = 0; i < link_nodes.getLength(); i++)
142 {
143 Element elem = (Element) link_nodes.item(i);
144 String type = elem.getAttribute("type");
145 if (type.equals("source"))
146 {
147 meta_names.add("assocfilepath");
148 meta_names.add("srclinkFile");
149 }
150 else if (type.equals("web"))
151 {
152 meta_names.add("weblink");
153 meta_names.add("webicon");
154 meta_names.add("/weblink");
155 }
156 else if (type.equals("equivdoc"))
157 {
158 getEquivLinkMeta = true;
159 }
160 }
161
162 // get all the metadata necessary for when the user has used "gsf:equivlink"
163 // so that we can build up the equivlink from the metadata components it needs
164 link_nodes = format.getElementsByTagNameNS(GSXML.GSF_NAMESPACE, "equivlinkgs3");
165 if (getEquivLinkMeta || link_nodes.getLength() > 0)
166 {
167 String[] equivlink_metanames = { "equivDocIcon", "equivDocLink", "/equivDocLink" };
168
169 for (int i = 0; i < equivlink_metanames.length; i++)
170 {
171 StringBuffer metadata = new StringBuffer();
172 metadata.append("all"); // this means the attr multiple = true;
173 metadata.append(GSConstants.META_RELATION_SEP);
174
175 metadata.append(GSConstants.META_SEPARATOR_SEP);
176 metadata.append(','); // attr separator = ","
177 metadata.append(GSConstants.META_SEPARATOR_SEP);
178 metadata.append(GSConstants.META_RELATION_SEP);
179
180 // the name of the metadata we're retrieving
181 metadata.append(equivlink_metanames[i]);
182 meta_names.add(metadata.toString());
183 }
184 }
185
186 if (format.getElementsByTagNameNS(GSXML.GSF_NAMESPACE, "image").getLength() > 0)
187 {
188 meta_names.add("Thumb");
189 meta_names.add("Screen");
190 meta_names.add("SourceFile");
191 }
192 }
193
194 protected Element createMetadataParamList(Document doc, HashSet<String> metadata_names)
195 {
196 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
197
198 Element param = null;
199 Iterator<String> i = metadata_names.iterator();
200 while (i.hasNext())
201 {
202 String name = i.next();
203 param = doc.createElement(GSXML.PARAM_ELEM);
204 param_list.appendChild(param);
205 param.setAttribute(GSXML.NAME_ATT, "metadata");
206 param.setAttribute(GSXML.VALUE_ATT, name);
207
208 }
209 return param_list;
210 }
211
212 protected boolean processErrorElements(Element message, Element page)
213 {
214 NodeList error_nodes = message.getElementsByTagName(GSXML.ERROR_ELEM);
215 if (error_nodes.getLength() == 0)
216 {
217 return false;
218 }
219 Document owner = page.getOwnerDocument();
220 for (int i = 0; i < error_nodes.getLength(); i++)
221 {
222 page.appendChild(owner.importNode(error_nodes.item(i), true));
223 }
224 return true;
225 }
226
227 /**
228 * Takes an XML element and adds the metadata of the current site to it.
229 * Useful for adding the current site's metadata to a response before
230 * sending it
231 *
232 * @param element
233 * the element to add site metadata to
234 * @param lang
235 * the current language
236 * @param uid
237 * the current user id
238 */
239 protected void addSiteMetadata(Element element, UserContext userContext)
240 {
241 Document doc = element.getOwnerDocument();
242
243 //ADD SITE METADATA
244 Element metadata_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "", userContext);
245 //create a hashmap of params
246 HashMap subset_params = new HashMap(1);
247 subset_params.put(GSXML.SUBSET_PARAM, GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER);
248 //create the element to put the params in
249 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
250 //put them in
251 GSXML.addParametersToList(doc, param_list, subset_params);
252 metadata_request.appendChild(param_list);
253 //create the message
254 Element metadata_message = doc.createElement(GSXML.MESSAGE_ELEM);
255 metadata_message.appendChild(metadata_request);
256 //get response
257 Element metadata_response_message = (Element) this.mr.process(metadata_message);
258 //drill down to response
259 Element metadata_response = (Element) GSXML.getChildByTagName(metadata_response_message, GSXML.RESPONSE_ELEM);
260 //merge in metadata
261 GSXML.mergeMetadataLists(element, metadata_response);
262 }
263
264 protected void addInterfaceOptions(Element elem)
265 {
266 Document doc = elem.getOwnerDocument();
267
268 Element documentOptionList = doc.createElement("interfaceOptions");
269 for (Object key : this.config_params.keySet())
270 {
271 Element option = doc.createElement("option");
272 option.setAttribute(GSXML.NAME_ATT, (String) key);
273 option.setAttribute(GSXML.VALUE_ATT, this.config_params.get(key).toString());
274 documentOptionList.appendChild(option);
275 }
276 elem.appendChild(elem.getOwnerDocument().importNode(documentOptionList, true));
277 }
278
279 protected Element getFormatInfo(String to, UserContext userContext)
280 {
281 // Eclipse call hierarchy shows the element returned from this method is
282 // subsequently used in a 'importNode'. For this reason it is safe here
283 // for call up our own document DOM.
284 //
285 // If this pattern changes for any reason, then the DOM will need to be
286 // passed in as a parameter
287
288 Document doc = this.converter.newDOM();
289
290 Element mr_format_message = doc.createElement(GSXML.MESSAGE_ELEM);
291 Element mr_format_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_FORMAT, to, userContext);
292 mr_format_message.appendChild(mr_format_request);
293
294 // process the message
295 Element mr_response_message = (Element) this.mr.process(mr_format_message);
296 // the response
297
298 Element format_response = (Element) GSXML.getChildByTagName(mr_response_message, GSXML.RESPONSE_ELEM);
299
300 Element format_elem = (Element) GSXML.getChildByTagName(format_response, GSXML.FORMAT_ELEM);
301 if (format_elem != null)
302 {
303 Element global_format_elem = (Element) GSXML.getChildByTagName(format_response, GSXML.GLOBAL_FORMAT_ELEM);
304 if (global_format_elem != null)
305 {
306 GSXSLT.mergeFormatElements(format_elem, global_format_elem, false);
307 }
308 }
309 return format_elem;
310 }
311}
Note: See TracBrowser for help on using the repository browser.