View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors.
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   * 
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.jdbc.delegate;
25  
26  import java.sql.DriverPropertyInfo;
27  import java.sql.SQLException;
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Properties;
36  import java.util.Set;
37  
38  import javax.jcr.RepositoryException;
39  import javax.jcr.Value;
40  import javax.jcr.nodetype.NodeDefinition;
41  import javax.jcr.nodetype.NodeType;
42  import javax.jcr.nodetype.NodeTypeIterator;
43  import javax.jcr.nodetype.PropertyDefinition;
44  import javax.jcr.query.QueryResult;
45  
46  import org.modeshape.jdbc.JcrDriver;
47  import org.modeshape.jdbc.JdbcI18n;
48  import org.modeshape.jdbc.JcrDriver.JcrContextFactory;
49  import org.modeshape.web.jcr.rest.client.domain.QueryRow;
50  import org.modeshape.web.jcr.rest.client.domain.Server;
51  import org.modeshape.web.jcr.rest.client.domain.Workspace;
52  import org.modeshape.web.jcr.rest.client.json.JsonRestClient;
53  
54  /**
55   * 
56   * The HTTPRepositoryDelegate provides remote Repository implementation to access the Jcr layer via HTTP lookup.
57   */
58  public class HttpRepositoryDelegate extends AbstractRepositoryDelegate {
59      private static final String HTTP_EXAMPLE_URL = JcrDriver.HTTP_URL_PREFIX + "{hostname}:{port}/{context root}";
60      
61      private static final String URL_PATH = "/jcr:system/jcr:nodeTypes";
62      private static final String URL_DEPTH = "?depth=5";
63      
64      private JsonRestClient restClient;
65      private Workspace workspace = null;
66      private Map<String, NodeType> nodeTypes;
67      
68      public HttpRepositoryDelegate(String url, Properties info, JcrContextFactory contextFactory) {
69      	super(url, info);
70      }
71         
72      @Override
73  	protected ConnectionInfo createConnectionInfo(String url, Properties info) {
74      	return new HttpConnectionInfo(url, info);
75      }
76  
77      @Override
78      public QueryResult execute(String query, String language) throws RepositoryException {
79         	LOGGER.trace("Executing query: {0}" + query );
80  
81         	try {
82  			List<QueryRow> results = this.restClient.query(workspace, language, query);
83  			Iterator<QueryRow> resultsIt = results.iterator();
84  			while (resultsIt.hasNext()) {
85  				final QueryRow row = resultsIt.next();
86  				
87  			}
88  			
89  		} catch (Exception e) {
90  		}
91  
92      	return null;
93      }
94      
95      /**
96  	 * {@inheritDoc}
97  	 *
98  	 * @see org.modeshape.jdbc.delegate.RepositoryDelegate#getDescriptor(java.lang.String)
99  	 */
100 	@Override
101 	public String getDescriptor(String descriptorKey) {
102 		return "";
103 	}
104        
105     @Override
106     public synchronized NodeType nodeType( String name ) throws RepositoryException {
107     	if (nodeTypes == null) nodeTypes();
108     	
109     	NodeType nodetype = nodeTypes.get(name);
110     	if (nodetype == null) {
111     		 throw new RepositoryException(JdbcI18n.unableToGetNodeType.text(name));		   		
112     	}
113     	
114     	return nodetype;
115     }
116        
117     @Override
118     public synchronized List<NodeType> nodeTypes() throws RepositoryException {
119     	if (nodeTypes == null) {
120 	    	Collection<org.modeshape.web.jcr.rest.client.domain.NodeType> results;
121 			try {
122 				results = this.restClient.getNodeTypes(workspace, URL_PATH, URL_DEPTH);
123 				if (results == null || results.size() == 0) {
124 				    String msg = JdbcI18n.noNodeTypesReturned.text(this.workspace.getServer().getUrl() + "/" + 
125 				    		this.workspace.getRepository().getName() + "/" +
126 				    		this.workspace.getName() + 
127 				    		URL_PATH +  URL_DEPTH);
128 				    throw new RepositoryException(msg);								
129 				}
130 				Map<String, NodeType> loadingNodeTypes = new HashMap<String, NodeType>(results.size());
131 				Iterator<org.modeshape.web.jcr.rest.client.domain.NodeType> resultsIt = results.iterator();
132 				while (resultsIt.hasNext()) {		
133 					org.modeshape.web.jcr.rest.client.domain.NodeType nodetype = resultsIt.next();
134 					if (nodetype != null) {
135 					
136 						HttpNodeType localnodetype = new HttpNodeType(nodetype);
137 					
138 						loadingNodeTypes.put(localnodetype.getName(), localnodetype);
139 					}
140 								
141 				}
142 				this.nodeTypes = loadingNodeTypes;
143 				
144 			} catch (Exception e) {
145 			    throw new RepositoryException(JdbcI18n.unableToGetNodeTypes.text(this.workspace.getRepository().getName()), e);			
146 			}
147     	}
148 		
149 		return new ArrayList<NodeType>(nodeTypes.values());
150     }
151 
152     @Override
153     protected  void createRepository() throws SQLException {
154        	LOGGER.debug("Creating repository for HttpRepositoryDelegte" );
155 
156        	ConnectionInfo info = getConnectionInfo();
157     	assert info != null;
158        	
159        	String path = info.getRepositoryPath();
160        	if (path == null) {
161        		throw new SQLException("Missing repo path from " +info.getUrl());
162        	}
163        	if (info.getUsername() == null) {
164        		throw new SQLException("Missing username from " +info.getUrl());
165        	}
166        	if (info.getPassword() == null) {
167        		throw new SQLException("Missing password path from " +info.getUrl());
168        	}
169        	if (info.getRepositoryName() == null) {
170        		throw new SQLException("Missing repo name from " +info.getUrl());
171        	}
172         Server server = new Server("http://" + path, info.getUsername(), new String(info.getPassword()));
173         org.modeshape.web.jcr.rest.client.domain.Repository repo = new org.modeshape.web.jcr.rest.client.domain.Repository(info.getRepositoryName(), server);
174         workspace = new Workspace(info.getWorkspaceName(), repo);
175 
176         restClient = new JsonRestClient();       
177  
178         // this is only a connection test to confirm a connection can be made and results can be obtained.
179         try {
180 			restClient.getRepositories(server ) ;			
181 		} catch (Exception e) {
182 			throw new SQLException(JdbcI18n.noRepositoryNamesFound.text(), e);
183 		}     
184         
185       	Set<String> repositoryNames = new HashSet<String>(1);
186      	repositoryNames.add(info.getRepositoryName());
187 	
188 		this.setRepositoryNames(repositoryNames);
189 		
190     }  
191     
192     /**
193      * 
194      * @see java.sql.Connection#isValid(int)
195      */
196     @Override
197     public boolean isValid( final int timeout ) throws RepositoryException {
198     	return false;
199     }
200     
201     /**
202      * {@inheritDoc}
203      * 
204      * @see java.sql.Connection#commit()
205      */
206     @Override
207     public void commit() throws RepositoryException {
208     }
209 
210     /**
211      * {@inheritDoc}
212      * 
213      * @see java.sql.Connection#rollback()
214      */
215     @Override
216     public void rollback() throws RepositoryException {
217     }
218     
219     /**
220      * {@inheritDoc}
221      * 
222      * @see java.sql.Connection#close()
223      */
224     @Override
225     public void close() {
226 		restClient = null;
227 		workspace = null;   	
228     	if (nodeTypes != null) nodeTypes.clear();
229     }
230     
231     class HttpConnectionInfo extends ConnectionInfo {
232  		/**
233 		 * @param url
234 		 * @param properties
235 		 */
236 		protected HttpConnectionInfo(String url, Properties properties) {
237 		    super(url, properties);
238 	
239 		}
240 		
241 	    @Override
242 		protected void init() {
243 	    	// parsing 2 ways of specifying the repository and workspace
244 	    	// 1)  defined using ?repositoryName
245 	    	// 2)  defined in the path  server:8080/modeshape-rest/respositoryName/workspaceName
246 	    	
247 	    	super.init();
248 	    	
249 	    	// if the workspace and/or repository name is not specified as a property on the url, 
250 	    	// then parse the url to obtain the values from the path, the url must be in the format:
251 	    	//   {hostname}:{port} / {context root} + / respositoryName / workspaceName 
252 
253 	    	StringBuilder url = new StringBuilder();
254 	    	String[] urlsections = repositoryPath.split("/");
255 	    	// if there are only 2 sections, then the url can have the workspace or repository name specified in the path
256 	    	if (urlsections.length < 3) {
257 	    		return;
258 	    	}
259 	    	
260 	    	// the assignment of url section is working back to front, this is so in cases where
261 	    	// the {context} is changed to be made up of multiple sections, instead of the default (modeshape-rest), the
262 	    	// workspace should be the last section (if exist) and the repository should be before the
263 	    	// workspace.
264 	    	int workspacePos = -1;
265 	    	int repositoryPos = -1;
266 	    	int repoPos = 1;
267 	    	if (this.getWorkspaceName() == null && urlsections.length > 3) {
268 	    		workspacePos = urlsections.length -1;
269 	    		String workspaceName = urlsections[workspacePos];	    		
270 	    		this.setWorkspaceName(workspaceName);
271 	    		// if workspace is found, then repository is assume in the prior section
272 	    		repoPos = 2;
273 	    		
274 	    	}
275 	    	if (this.getRepositoryName() == null && urlsections.length > 2) {
276 	    		repositoryPos = urlsections.length - repoPos;
277 	    		String repositoryName = urlsections[repositoryPos];
278 	    		this.setRepositoryName(repositoryName);
279 	    	}
280 	    	
281 	    	// rebuild the url without the repositoryName or WorkspaceName because 
282 	    	// the createConnection() needs these separated.
283 	    	for (int i = 0; i < repositoryPos; i++) {
284 	    		url.append(urlsections[i]);
285 	    		if (i < repositoryPos -1) {
286 	    			url.append("/");
287 	    		}
288 	    	}
289 	    	
290 	    	this.repositoryPath = url.toString();
291 
292 	    }
293 		
294 		@Override
295 	    public String getUrlExample() {
296 	    	return HTTP_EXAMPLE_URL;
297 	    }
298 		
299 		@Override
300 	    public String getUrlPrefix() {
301 			return JcrDriver.HTTP_URL_PREFIX;
302 		}
303 		
304 		@Override		
305 		protected void addUrlPropertyInfo(List<DriverPropertyInfo> results) {
306 			// if the repository path doesn't have at least the {context} 
307 			// example:  server:8080/modeshape-rest   where modeshape-rest is the context,
308 			// then the URL is considered invalid.
309 			if (repositoryPath.indexOf("/") == -1) {
310 				setUrl(null);
311 			}
312 			super.addUrlPropertyInfo(results);
313 		}
314 
315     }
316     
317     
318     
319     protected class HttpNodeType implements javax.jcr.nodetype.NodeType {
320     	private org.modeshape.web.jcr.rest.client.domain.NodeType restnodetype;
321      	
322     	public HttpNodeType(org.modeshape.web.jcr.rest.client.domain.NodeType nodetype) {
323     		assert nodetype != null;
324     		restnodetype = nodetype;    		
325     	}
326     	
327     	public void setNodeType(org.modeshape.web.jcr.rest.client.domain.NodeType nodetype) {
328     		restnodetype = nodetype;
329     	}
330     	
331     	public org.modeshape.web.jcr.rest.client.domain.NodeType getNodeType() {
332     		return restnodetype;
333     	}
334     	
335     	public boolean hasProperties() {
336     		return (restnodetype.getProperties().size() > 0 ? true : false);
337     	}
338 
339 		@Override
340 		public String getName() {
341 			return restnodetype.getName();
342 		}
343 
344 		@Override
345 		public String getPrimaryItemName() {
346 			return restnodetype.getProperty("jcr:primaryItemName");
347 		}
348 
349 		@Override
350 		public boolean isMixin() {			
351 			return convertBoolean("jcr:isMixin");
352 		}
353 
354 		@Override
355 		public boolean isQueryable() {
356 			return convertBoolean("jcr:isQueryable");
357 		}
358 
359 		/**
360 		 * {@inheritDoc}
361 		 *
362 		 * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String)
363 		 */
364 		@Override
365 		public boolean canAddChildNode(String arg0) {
366 			return false;
367 		}
368 
369 		/**
370 		 * {@inheritDoc}
371 		 *
372 		 * @see javax.jcr.nodetype.NodeType#canAddChildNode(java.lang.String, java.lang.String)
373 		 */
374 		@Override
375 		public boolean canAddChildNode(String arg0, String arg1) {
376 			return false;
377 		}
378 
379 		/**
380 		 * {@inheritDoc}
381 		 *
382 		 * @see javax.jcr.nodetype.NodeType#canRemoveItem(java.lang.String)
383 		 */
384 		@Override
385 		public boolean canRemoveItem(String arg0) {
386 			return false;
387 		}
388 
389 		/**
390 		 * {@inheritDoc}
391 		 *
392 		 * @see javax.jcr.nodetype.NodeType#canRemoveNode(java.lang.String)
393 		 */
394 		@Override
395 		public boolean canRemoveNode(String arg0) {
396 			return false;
397 		}
398 
399 		/**
400 		 * {@inheritDoc}
401 		 *
402 		 * @see javax.jcr.nodetype.NodeType#canRemoveProperty(java.lang.String)
403 		 */
404 		@Override
405 		public boolean canRemoveProperty(String arg0) {
406 			return false;
407 		}
408 
409 		/**
410 		 * {@inheritDoc}
411 		 *
412 		 * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value)
413 		 */
414 		@Override
415 		public boolean canSetProperty(String arg0, Value arg1) {
416 			return false;
417 		}
418 
419 		/**
420 		 * {@inheritDoc}
421 		 *
422 		 * @see javax.jcr.nodetype.NodeType#canSetProperty(java.lang.String, javax.jcr.Value[])
423 		 */
424 		@Override
425 		public boolean canSetProperty(String arg0, Value[] arg1) {
426 			return false;
427 		}
428 
429 		/**
430 		 * {@inheritDoc}
431 		 *
432 		 * @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions()
433 		 */
434 		@Override
435 		public NodeDefinition[] getChildNodeDefinitions() {
436 			return null;
437 		}
438 
439 		/**
440 		 * {@inheritDoc}
441 		 *
442 		 * @see javax.jcr.nodetype.NodeType#getDeclaredSubtypes()
443 		 */
444 		@Override
445 		public NodeTypeIterator getDeclaredSubtypes() {
446 			return null;
447 		}
448 
449 		/**
450 		 * {@inheritDoc}
451 		 *
452 		 * @see javax.jcr.nodetype.NodeType#getDeclaredSupertypes()
453 		 */
454 		@Override
455 		public javax.jcr.nodetype.NodeType[] getDeclaredSupertypes() {
456 			return null;
457 		}
458 
459 		/**
460 		 * {@inheritDoc}
461 		 *
462 		 * @see javax.jcr.nodetype.NodeType#getSubtypes()
463 		 */
464 		@Override
465 		public NodeTypeIterator getSubtypes() {
466 			return null;
467 		}
468 
469 		/**
470 		 * {@inheritDoc}
471 		 *
472 		 * @see javax.jcr.nodetype.NodeType#getSupertypes()
473 		 */
474 		@Override
475 		public javax.jcr.nodetype.NodeType[] getSupertypes() {
476 			return null;
477 		}
478 
479 		/**
480 		 * {@inheritDoc}
481 		 *
482 		 * @see javax.jcr.nodetype.NodeType#isNodeType(java.lang.String)
483 		 */
484 		@Override
485 		public boolean isNodeType(String arg0) {		
486 			String value = restnodetype.getProperty("jcr:nodeTypeName");
487 			return (value != null && arg0.equals(value));
488 
489 		}
490 
491 		/**
492 		 * {@inheritDoc}
493 		 *
494 		 * @see javax.jcr.nodetype.NodeTypeDefinition#getDeclaredChildNodeDefinitions()
495 		 */
496 		@Override
497 		public NodeDefinition[] getDeclaredChildNodeDefinitions() {
498 			return null;
499 		}
500 
501 		/**
502 		 * {@inheritDoc}
503 		 *
504 		 * @see javax.jcr.nodetype.NodeTypeDefinition#getDeclaredPropertyDefinitions()
505 		 */
506 		@Override
507 		public PropertyDefinition[] getDeclaredPropertyDefinitions() {
508 			return null;
509 		}
510 
511 		/**
512 		 * {@inheritDoc}
513 		 *
514 		 * @see javax.jcr.nodetype.NodeTypeDefinition#getDeclaredSupertypeNames()
515 		 */
516 		@Override
517 		public String[] getDeclaredSupertypeNames() {
518 //			String value = restnodetype.getProperty("jcr:supertypes"); -- array
519 //			return new String[] {value};
520 			return null;
521 		}
522 
523 		/**
524 		 * {@inheritDoc}
525 		 *
526 		 * @see javax.jcr.nodetype.NodeTypeDefinition#hasOrderableChildNodes()
527 		 */
528 		@Override
529 		public boolean hasOrderableChildNodes() {
530 			return convertBoolean("jcr:hasOrderableChildNodes");		
531 		}
532 
533 		/**
534 		 * {@inheritDoc}
535 		 *
536 		 * @see javax.jcr.nodetype.NodeTypeDefinition#isAbstract()
537 		 */
538 		@Override
539 		public boolean isAbstract() {
540 			return convertBoolean("jcr:isAbstract");		
541 		}
542 
543 		/**
544 		 * {@inheritDoc}
545 		 *
546 		 * @see javax.jcr.nodetype.NodeType#getPropertyDefinitions()
547 		 */
548 		@Override
549 		public PropertyDefinition[] getPropertyDefinitions() {
550 			PropertyDefinition[] defns = null;
551 			int cnt  = restnodetype.getPropertyDefinitions().size() + restnodetype.getChildNodeDefinitions().size();
552 			defns = new PropertyDefinition[cnt];
553 			int i = 0;
554 			if (restnodetype.getPropertyDefinitions() != null) {
555 				for (Iterator<org.modeshape.web.jcr.rest.client.domain.NodeType> it=restnodetype.getPropertyDefinitions().iterator(); it.hasNext();){
556 					org.modeshape.web.jcr.rest.client.domain.NodeType nt = it.next();
557 					HttpPropertyDefinition propDefn = new HttpPropertyDefinition(nt.getName(), nt.getProperties()) ;
558 					defns[i] = propDefn;
559 					i++;
560 				}
561 			}
562 			
563 			if (restnodetype.getChildNodeDefinitions() != null) {
564 				for (Iterator<org.modeshape.web.jcr.rest.client.domain.NodeType> it=restnodetype.getChildNodeDefinitions().iterator(); it.hasNext();){
565 					org.modeshape.web.jcr.rest.client.domain.NodeType nt = it.next();
566 					HttpPropertyDefinition propDefn = new HttpPropertyDefinition(nt.getName(), nt.getProperties()) ;
567 					defns[i] = propDefn;
568 					i++;
569 				}
570 			}
571 			return defns;
572 		}
573     	
574 		
575 		private boolean convertBoolean(String key ) {
576 			String value = restnodetype.getProperty(key);
577 			if (value == null || value.equalsIgnoreCase("false")) return false;			
578 			return true;
579 		}    	
580     }
581     
582     protected class HttpPropertyDefinition implements PropertyDefinition {
583     	private Properties properties = null;
584     	private String name;
585     	
586     	public HttpPropertyDefinition(String name, Properties nodeTypeProperties) {
587     		this.properties = nodeTypeProperties;
588     		this.name = name;
589     	}
590     	
591 
592 		/**
593 		 * {@inheritDoc}
594 		 *
595 		 * @see javax.jcr.nodetype.PropertyDefinition#getAvailableQueryOperators()
596 		 */
597 		@Override
598 		public String[] getAvailableQueryOperators() {
599 			return null;
600 		}
601 
602 		/**
603 		 * {@inheritDoc}
604 		 *
605 		 * @see javax.jcr.nodetype.PropertyDefinition#getDefaultValues()
606 		 */
607 		@Override
608 		public Value[] getDefaultValues() {
609 			return null;
610 		}
611 
612 		/**
613 		 * {@inheritDoc}
614 		 *
615 		 * @see javax.jcr.nodetype.PropertyDefinition#getRequiredType()
616 		 */
617 		@Override
618 		public int getRequiredType() {
619 			return 0;
620 //			String value = properties.getProperty("jcr:requiredType");
621 //			if (value == null) return PropertyDefinition.
622 		}
623 
624 		/**
625 		 * {@inheritDoc}
626 		 *
627 		 * @see javax.jcr.nodetype.PropertyDefinition#getValueConstraints()
628 		 */
629 		@Override
630 		public String[] getValueConstraints() {
631 			return null;
632 		}
633 
634 		/**
635 		 * {@inheritDoc}
636 		 *
637 		 * @see javax.jcr.nodetype.PropertyDefinition#isFullTextSearchable()
638 		 */
639 		@Override
640 		public boolean isFullTextSearchable() {
641 			return false;
642 		}
643 
644 		/**
645 		 * {@inheritDoc}
646 		 *
647 		 * @see javax.jcr.nodetype.PropertyDefinition#isMultiple()
648 		 */
649 		@Override
650 		public boolean isMultiple() {
651 			return false;
652 		}
653 
654 		/**
655 		 * {@inheritDoc}
656 		 *
657 		 * @see javax.jcr.nodetype.PropertyDefinition#isQueryOrderable()
658 		 */
659 		@Override
660 		public boolean isQueryOrderable() {
661 			return true;
662 	//		return convertBoolean("jcr:isQueryable");
663 		}
664 
665 		/**
666 		 * {@inheritDoc}
667 		 *
668 		 * @see javax.jcr.nodetype.ItemDefinition#getDeclaringNodeType()
669 		 */
670 		@Override
671 		public NodeType getDeclaringNodeType() {
672 			return null;
673 		}
674 
675 		/**
676 		 * {@inheritDoc}
677 		 *
678 		 * @see javax.jcr.nodetype.ItemDefinition#getName()
679 		 */
680 		@Override
681 		public String getName() {
682 			return  name;
683 		}
684 
685 		/**
686 		 * {@inheritDoc}
687 		 *
688 		 * @see javax.jcr.nodetype.ItemDefinition#getOnParentVersion()
689 		 */
690 		@Override
691 		public int getOnParentVersion() {
692 			return 0;
693 		}
694 
695 		/**
696 		 * {@inheritDoc}
697 		 *
698 		 * @see javax.jcr.nodetype.ItemDefinition#isAutoCreated()
699 		 */
700 		@Override
701 		public boolean isAutoCreated() {
702 			return convertBoolean("jcr:autoCreated");
703 		}
704 
705 		/**
706 		 * {@inheritDoc}
707 		 *
708 		 * @see javax.jcr.nodetype.ItemDefinition#isMandatory()
709 		 */
710 		@Override
711 		public boolean isMandatory() {
712 			return convertBoolean("jcr:mandatory");
713 		}
714 
715 		/**
716 		 * {@inheritDoc}
717 		 *
718 		 * @see javax.jcr.nodetype.ItemDefinition#isProtected()
719 		 */
720 		@Override
721 		public boolean isProtected() {
722 			return convertBoolean("jcr:protected");
723 		}
724 		
725 		
726 		private boolean convertBoolean(String key ) {
727 			String value = properties.getProperty(key);
728 			if (value == null || value.equalsIgnoreCase("false")) return false;			
729 			return true;
730 		} 
731     	
732     }
733   
734 }