001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.externaldata.data.jcr;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.HashMap;
021import java.util.List;
022import java.util.Map;
023
024import javax.jcr.Node;
025import javax.jcr.NodeIterator;
026import javax.jcr.PathNotFoundException;
027import javax.jcr.Property;
028import javax.jcr.PropertyIterator;
029import javax.jcr.Repository;
030import javax.jcr.RepositoryException;
031import javax.jcr.Session;
032import javax.jcr.Value;
033
034import org.apache.avalon.framework.component.Component;
035import org.apache.avalon.framework.logger.AbstractLogEnabled;
036import org.apache.avalon.framework.service.ServiceException;
037import org.apache.avalon.framework.service.ServiceManager;
038import org.apache.avalon.framework.service.Serviceable;
039import org.apache.commons.lang.StringUtils;
040import org.apache.jackrabbit.JcrConstants;
041
042import org.ametys.cms.FilterNameHelper;
043import org.ametys.core.datasource.DataSourceClientInteraction.DataSourceType;
044import org.ametys.core.observation.Event;
045import org.ametys.core.observation.ObservationManager;
046import org.ametys.core.ui.Callable;
047import org.ametys.core.user.CurrentUserProvider;
048import org.ametys.plugins.externaldata.cache.ObservationConstants;
049import org.ametys.plugins.externaldata.data.DataInclusionException;
050import org.ametys.plugins.externaldata.data.DataSourceFactory;
051import org.ametys.plugins.externaldata.data.DataSourceFactoryExtensionPoint;
052import org.ametys.plugins.externaldata.data.Query;
053import org.ametys.plugins.externaldata.data.Query.ResultType;
054import org.ametys.plugins.externaldata.data.QueryDao;
055import org.ametys.plugins.externaldata.data.QueryResult;
056import org.ametys.plugins.repository.AmetysObjectResolver;
057import org.ametys.plugins.repository.AmetysRepositoryException;
058import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
059import org.ametys.plugins.repository.RepositoryConstants;
060import org.ametys.plugins.repository.jcr.JCRAmetysObject;
061import org.ametys.plugins.repository.provider.AbstractRepository;
062import org.ametys.web.repository.site.SiteManager;
063
064/**
065 * JCR implementation of the Query DAO.
066 * This class manages DataSource and Query objects, storing them in the JCR repository.
067 */
068public class JcrQueryDao extends AbstractLogEnabled implements QueryDao, Serviceable, Component
069{
070    
071    /** JCR relative path to root node. */
072    public static final String ROOT_REPO = AmetysObjectResolver.ROOT_REPO;
073    
074    /** Plugins root node name. */
075    public static final String PLUGINS_NODE = "ametys-internal:plugins";
076    
077    /** Plugin root node name. */
078    public static final String PLUGIN_NODE = "external-data";
079    
080    /** Data sources node name. */
081    public static final String DATASOURCES_NODE = RepositoryConstants.NAMESPACE_PREFIX + ":datasources";
082    
083    /** Queries node name. */
084    public static final String QUERIES_NODE = RepositoryConstants.NAMESPACE_PREFIX + ":queries";
085    
086    /** "Name" property name. */
087    public static final String PROPERTY_NAME = RepositoryConstants.NAMESPACE_PREFIX + ":name";
088    
089    /** "Description" property name. */
090    public static final String PROPERTY_DESCRIPTION = RepositoryConstants.NAMESPACE_PREFIX + ":description";
091    
092    /** "Type" property name. */
093    public static final String PROPERTY_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":type";
094    
095    /** "Type" property name. */
096    public static final String PROPERTY_DATASOURCE = RepositoryConstants.NAMESPACE_PREFIX + ":dataSourceId";
097    
098    /** Configuration properties prefix. */
099    public static final String PROPERTY_CONF_PREFIX = RepositoryConstants.NAMESPACE_PREFIX + ":conf-";
100    
101    /** Query "query string" property name. */
102    public static final String QUERY_PROPERTY_QUERYSTRING = RepositoryConstants.NAMESPACE_PREFIX + ":queryString";
103    
104    /** Query "parameters" property name. */
105    public static final String QUERY_PROPERTY_PARAMETERS = RepositoryConstants.NAMESPACE_PREFIX + ":parameters";
106    
107    /** Query "resultType" property name. */
108    public static final String QUERY_PROPERTY_RESULTTYPE = RepositoryConstants.NAMESPACE_PREFIX + ":resultType";
109    
110    /** The Data Source factory extension point. */
111    protected DataSourceFactoryExtensionPoint _dataSourceFactoryEP;
112    
113    /** The JCR repository. */
114    protected Repository _repository;
115    
116    /** The Site manager. */
117    protected SiteManager _siteManager;
118
119    /** The current user provider */
120    protected CurrentUserProvider _currentUserProvider;
121
122    /** The observation manager */
123    protected ObservationManager _observationManager;
124    
125    @Override
126    public void service(ServiceManager serviceManager) throws ServiceException
127    {
128        _dataSourceFactoryEP = (DataSourceFactoryExtensionPoint) serviceManager.lookup(DataSourceFactoryExtensionPoint.ROLE);
129        _repository = (Repository) serviceManager.lookup(AbstractRepository.ROLE);
130        _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
131        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
132        _observationManager = (ObservationManager) serviceManager.lookup(ObservationManager.ROLE);
133    }
134    
135    @Override
136    public Map<String, Query> getQueries(String siteName) throws DataInclusionException
137    {
138        try
139        {
140            Map<String, Query> queries = new HashMap<>();
141            
142            Node queriesNode = _getQueriesNode(siteName);
143            
144            NodeIterator queryNodes = queriesNode.getNodes();
145            while (queryNodes.hasNext())
146            {
147                Node node = queryNodes.nextNode();
148                
149                // Extract a Query object from the node and add it to the map.
150                Query query = _extractQuery(node);
151                queries.put(query.getId(), query);
152            }
153            
154            return queries;
155        }
156        catch (RepositoryException e)
157        {
158            throw new DataInclusionException("Error getting the data sources", e);
159        }
160    }
161    
162    @Override
163    public Map<String, Query> getQueries(String siteName, DataSourceType type) throws DataInclusionException
164    {
165        return getDataSourceQueries(siteName, null, type, null);
166    }
167    
168    @Override
169    public Map<String, Query> getDataSourceQueries(String siteName, String dataSourceId) throws DataInclusionException
170    {
171        return getDataSourceQueries(siteName, dataSourceId, null);
172    }
173    
174    @Override
175    public Map<String, Query> getDataSourceQueries(String siteName, String dataSourceId, ResultType resultType) throws DataInclusionException
176    {
177        return getDataSourceQueries(siteName, dataSourceId, null, resultType);
178    }
179    
180    @Override
181    public Map<String, Query> getDataSourceQueries(String siteName, String dataSourceId, DataSourceType dataSourceType, ResultType resultType) throws DataInclusionException
182    {
183        try
184        {
185            // TODO Should be a made with a query instead of filtering.
186            Map<String, Query> queries = new HashMap<>();
187            
188            Node queriesNode = _getQueriesNode(siteName);
189            
190            NodeIterator queryNodes = queriesNode.getNodes();
191            while (queryNodes.hasNext())
192            {
193                Node node = queryNodes.nextNode();
194                
195                // Extract a Query object from the node and add it to the map.
196                Query query = _extractQuery(node);
197                if (_matchQuery(query, dataSourceId, dataSourceType, resultType))
198                {
199                    queries.put(query.getId(), query);
200                }
201            }
202            
203            return queries;
204        }
205        catch (RepositoryException e)
206        {
207            throw new DataInclusionException("Error getting the data sources", e);
208        }
209    }
210    
211    private boolean _matchQuery (Query query, String dataSourceId, DataSourceType dsType, ResultType resultType)
212    {
213        if ((dataSourceId == null || query.getDataSourceId().equals(dataSourceId))
214                && (dsType == null || query.getType().equals(dsType))
215                && (resultType == null || resultType.equals(query.getResultType())))
216        {
217            return true;
218        }
219        
220        return false;
221    }
222    
223    @Override
224    public Query getQuery(String siteName, String id) throws DataInclusionException
225    {
226        try
227        {
228            Query query = null;
229            
230            Node queriesNode = _getQueriesNode(siteName);
231            
232            if (queriesNode.hasNode(id))
233            {
234                Node node = queriesNode.getNode(id);
235                
236                // Extract a Query object from the node.
237                query = _extractQuery(node);
238            }
239            
240            return query;
241        }
242        catch (RepositoryException e)
243        {
244            throw new DataInclusionException("Error getting the data source of id " + id, e);
245        }
246    }
247    
248    @Override
249    @Callable
250    public Map<String, Object> getQueryProperties(String id, String siteName) throws DataInclusionException
251    {
252        Map<String, Object> infos = new HashMap<>();
253        
254        Query query = getQuery(siteName, id);
255        if (query != null)
256        {
257            infos.put("id", query.getId());
258            infos.put("name", query.getName());
259            infos.put("description", query.getDescription());
260            infos.put("type", query.getType());
261            infos.put("resultType", query.getResultType().name());
262            
263            Map<String, String> additionalConf = query.getAdditionalConfiguration();
264            for (String confName : additionalConf.keySet())
265            {
266                String value = additionalConf.get(confName);
267                if (value != null)
268                {
269                    infos.put(confName, value);
270                }
271            }
272            
273            infos.put("parameters", query.getParameters());
274            
275            infos.put("dataSourceId", query.getDataSourceId());
276        }
277        
278        return infos;
279    }
280    
281    @Override
282    @Callable
283    public Map<String, String> addQuery(String siteName, Map<String, Object> parameters) throws Exception
284    {
285        Map<String, String> result = new HashMap<>();
286        
287        String type = (String) parameters.get("type");
288        String name = (String) parameters.get("name");
289        String description = (String) parameters.get("description");
290        String resultTypeStr = (String) parameters.get("resultType");
291        ResultType resultType = _getResultType(resultTypeStr, ResultType.MULTIPLE);
292        String dataSourceId = (String) parameters.get("dataSourceId");
293        
294        DataSourceFactory<Query, QueryResult> factory = _dataSourceFactoryEP.getFactory(DataSourceType.valueOf(type));
295        if (factory == null)
296        {
297            getLogger().error("Impossible to get a factory to handle the type : " + type);
298            result.put("error", "unknown-type");
299            return result;
300        }
301        Collection<String> params = factory.getQueryConfigurationParameters(type);
302        
303        Map<String, String> additionalConfiguration = new HashMap<>();
304        for (String paramName : params)
305        {
306            String value = (String) parameters.get(paramName);
307            if (value != null)
308            {
309                additionalConfiguration.put(paramName, value);
310            }
311        }
312        
313        try
314        {
315            Query query = factory.buildQuery(null, type, name, description, resultType, dataSourceId, additionalConfiguration);
316            
317            String id = addQuery(siteName, query);
318            
319            if (getLogger().isDebugEnabled())
320            {
321                getLogger().debug("Query created : got id " + id);
322            }
323            
324            result.put("id", id);
325            result.put("parentId", dataSourceId);
326            result.put("type", type);
327        }
328        catch (DataInclusionException e)
329        {
330            getLogger().error("Error saving the query", e);
331            result.put("error", "add-query-error");
332        }
333        
334        return result;
335    }
336    
337    @Override
338    public String addQuery(String siteName, Query query) throws DataInclusionException
339    {
340        try
341        {
342            Node queriesNode = _getQueriesNode(siteName);
343            
344            String name = query.getName();
345            if (StringUtils.isBlank(name))
346            {
347                throw new DataInclusionException("Query name can't be blank.");
348            }
349            
350            String nodeName = FilterNameHelper.filterName(name);
351            String notExistingNodeName = _getNotExistingNodeName(queriesNode, nodeName);
352            
353            // TODO Create a query JCR node type.
354            Node node = queriesNode.addNode(notExistingNodeName);
355            String id = node.getName();
356            node.addMixin(JcrConstants.MIX_REFERENCEABLE);
357            
358            _fillQueryNode(query, node);
359            
360            queriesNode.getSession().save();
361            
362            return id;
363        }
364        catch (RepositoryException e)
365        {
366            throw new DataInclusionException("Error adding a Query", e);
367        }
368    }
369    
370    @Override
371    @Callable
372    public Map<String, String> updateQuery(String siteName, Map<String, Object> parameters) throws Exception
373    {
374        Map<String, String> result = new HashMap<>();
375        
376        String id = (String) parameters.get("id");
377        String type = (String) parameters.get("type");
378        String name = (String) parameters.get("name");
379        String description = (String) parameters.get("description");
380        String resultTypeStr = (String) parameters.get("resultType");
381        ResultType resultType = _getResultType(resultTypeStr, ResultType.MULTIPLE);
382        
383        DataSourceFactory<Query, QueryResult> factory = _dataSourceFactoryEP.getFactory(DataSourceType.valueOf(type));
384        Collection<String> params = factory.getQueryConfigurationParameters(type);
385        
386        Map<String, String> additionalConfiguration = new HashMap<>();
387        for (String paramName : params)
388        {
389            String value = (String) parameters.get(paramName);
390            if (value != null)
391            {
392                additionalConfiguration.put(paramName, value);
393            }
394        }
395        
396        try
397        {
398            Query query = factory.buildQuery(id, type, name, description, resultType, null, additionalConfiguration);
399            
400            updateQuery(siteName, query);
401            
402            if (getLogger().isDebugEnabled())
403            {
404                getLogger().debug("Query updated : id " + query.getId());
405            }
406            
407            Map<String, Object> eventParams = new HashMap<>();
408            eventParams.put(ObservationConstants.ARGS_QUERY_ID, query.getId());
409            _observationManager.notify(new Event(ObservationConstants.EVENT_QUERY_UPDATED, _currentUserProvider.getUser(), eventParams));
410            
411            result.put("id", id);
412            result.put("type", type);
413        }
414        catch (DataInclusionException e)
415        {
416            getLogger().error("Error updating the query", e);
417            result.put("error", "update-query-error");
418        }
419        
420        return result;
421    }
422    
423    @Override
424    public void updateQuery(String siteName, Query query) throws DataInclusionException
425    {
426        String id = query.getId();
427        
428        try
429        {
430            Node queriesNode = _getQueriesNode(siteName);
431            
432            if (!queriesNode.hasNode(id))
433            {
434                throw new DataInclusionException("No query exists with id " + id);
435            }
436            
437            Node node = queriesNode.getNode(id);
438            
439            String name = query.getName();
440            if (StringUtils.isBlank(name))
441            {
442                throw new DataInclusionException("Name can't be blank.");
443            }
444            
445            _fillQueryNode(query, node);
446            
447            node.getSession().save();
448        }
449        catch (RepositoryException e)
450        {
451            throw new DataInclusionException("Error updating the Data Source of id " + id, e);
452        }
453    }
454    
455    @Override
456    @Callable
457    public Map<String, String> deleteQuery(String siteName, String id) throws Exception
458    {
459        Map<String, String> result = new HashMap<>();
460        
461        try
462        {
463            removeQuery(siteName, id);
464            
465            if (getLogger().isDebugEnabled())
466            {
467                getLogger().debug("Query id " + id + " deleted.");
468            }
469            
470            Map<String, Object> eventParams = new HashMap<>();
471            eventParams.put(ObservationConstants.ARGS_QUERY_ID, id);
472            _observationManager.notify(new Event(ObservationConstants.EVENT_QUERY_DELETED, _currentUserProvider.getUser(), eventParams));
473            
474            result.put("id", id);
475        }
476        catch (DataInclusionException e)
477        {
478            getLogger().error("Error deleting the query", e);
479            result.put("error", "delete-query-error");
480        }
481        
482        return result;
483    }
484    
485    @Override
486    public void removeQuery(String siteName, String id) throws DataInclusionException
487    {
488        try
489        {
490            Node queriesNode = _getQueriesNode(siteName);
491            
492            if (!queriesNode.hasNode(id))
493            {
494                throw new DataInclusionException("No data source exists with id " + id);
495            }
496            
497            queriesNode.getNode(id).remove();
498            
499            queriesNode.getSession().save();
500        }
501        catch (RepositoryException e)
502        {
503            throw new DataInclusionException("Error updating the Data Source of id " + id, e);
504        }
505    }
506    
507    /**
508     * Create a data source from a node.
509     * @param node the data source node.
510     * @return the data source.
511     * @throws RepositoryException if an error occurs when exploring the repository
512     * @throws DataInclusionException if an error occurs while manipulating the data sources
513     */
514    protected Query _extractQuery(Node node) throws RepositoryException, DataInclusionException
515    {
516        Query query = null;
517        
518        String id = node.getName();
519        String type = _getSingleProperty(node, PROPERTY_TYPE, "");
520        String name = _getSingleProperty(node, PROPERTY_NAME, "");
521        String description = _getSingleProperty(node, PROPERTY_DESCRIPTION, "");
522        ResultType resultType = _getResultType(node, ResultType.MULTIPLE);
523        String dataSourceId = _getSingleProperty(node, PROPERTY_DATASOURCE, "");
524        Map<String, String> additionalConf = _getAdditionalConf(node);
525        
526        DataSourceFactory<Query, QueryResult> factory = _dataSourceFactoryEP.getFactory(DataSourceType.valueOf(type));
527        
528        if (factory != null)
529        {
530            query = factory.buildQuery(id, type, name, description, resultType, dataSourceId, additionalConf);
531        }
532        else
533        {
534            throw new DataInclusionException("Unknown query type.");
535        }
536        
537        return query;
538    }
539    
540    /**
541     * Get a single property value.
542     * @param node the JCR node.
543     * @param propertyName the name of the property to get.
544     * @param defaultValue the default value if the property does not exist.
545     * @return the single property value.
546     * @throws RepositoryException if a repository error occurs.
547     */
548    protected String _getSingleProperty(Node node, String propertyName, String defaultValue) throws RepositoryException
549    {
550        String value = defaultValue;
551        
552        if (node.hasProperty(propertyName))
553        {
554            value = node.getProperty(propertyName).getString();
555        }
556        
557        return value;
558    }
559    
560    /**
561     * Get a a result type
562     * @param value the value
563     * @param defaultValue the default result type
564     * @return the result type for the value
565     * @throws RepositoryException if an error occurs when exploring the repository
566     */
567    protected ResultType _getResultType(String value, ResultType defaultValue) throws RepositoryException
568    {
569        ResultType result = defaultValue;
570        try
571        {
572            result = ResultType.valueOf(value);
573        }
574        catch (Exception e)
575        {
576            // Ignore
577        }
578        
579        return result;
580    }
581    
582    /**
583     * Get a single property value.
584     * @param node the node
585     * @param defaultValue the default result type
586     * @return the result type for the value
587     * @throws RepositoryException if an error occurs when exploring the repository
588     */
589    protected ResultType _getResultType(Node node, ResultType defaultValue) throws RepositoryException
590    {
591        ResultType result = defaultValue;
592        if (node.hasProperty(QUERY_PROPERTY_RESULTTYPE))
593        {
594            String value = node.getProperty(QUERY_PROPERTY_RESULTTYPE).getString();
595            try
596            {
597                result = ResultType.valueOf(value);
598            }
599            catch (Exception e)
600            {
601                // Ignore
602            }
603        }
604        
605        return result;
606    }
607    
608    /**
609     * Get the values of a string array property.
610     * @param node the node.
611     * @param propertyName the name of the property to get.
612     * @return the values.
613     * @throws RepositoryException if a repository error occurs.
614     */
615    protected Collection<String> _getMultipleProperty(Node node, String propertyName) throws RepositoryException
616    {
617        List<String> values = new ArrayList<>();
618        
619        if (node.hasProperty(propertyName))
620        {
621            Value[] propertyValues = node.getProperty(propertyName).getValues();
622            for (Value value : propertyValues)
623            {
624                values.add(value.getString());
625            }
626        }
627        
628        return values;
629    }
630    
631    /**
632     * Get additional configuration from properties.
633     * @param node the node 
634     * @return the additional configuration as a Map.
635     * @throws RepositoryException if a repository error occurs.
636     */
637    protected Map<String, String> _getAdditionalConf(Node node) throws RepositoryException
638    {
639        Map<String, String> values = new HashMap<>();
640        
641        PropertyIterator propertyIt = node.getProperties(PROPERTY_CONF_PREFIX + "*");
642        while (propertyIt.hasNext())
643        {
644            Property property = propertyIt.nextProperty();
645            String propName = property.getName();
646            String name = propName.substring(PROPERTY_CONF_PREFIX.length(), propName.length());
647            String value = property.getString();
648            
649            values.put(name, value);
650        }
651        
652        return values;
653    }
654    
655    /**
656     * Store the query properties into the JCR node.
657     * @param query the querys
658     * @param node the query node 
659     * @throws RepositoryException if a repository error occurs.
660     */
661    protected void _fillQueryNode(Query query, Node node) throws RepositoryException
662    {
663        node.setProperty(PROPERTY_NAME, query.getName());
664        node.setProperty(PROPERTY_DESCRIPTION, query.getDescription());
665        node.setProperty(PROPERTY_TYPE, query.getType().name());
666        node.setProperty(QUERY_PROPERTY_RESULTTYPE, query.getResultType().name());
667        if (query.getDataSourceId() != null)
668        {
669            node.setProperty(PROPERTY_DATASOURCE, query.getDataSourceId());
670        }
671        
672        Map<String, String> additionalConf = query.getAdditionalConfiguration();
673        for (String confName : additionalConf.keySet())
674        {
675            String value = additionalConf.get(confName);
676            node.setProperty(PROPERTY_CONF_PREFIX + confName, value);
677        }
678    }
679    
680    /**
681     * Get a name for a node which doesn't already exist in this node.
682     * @param container the container node.
683     * @param baseName the base wanted node name.
684     * @return the name, free to be taken.
685     * @throws RepositoryException if a repository error occurs.
686     */
687    protected String _getNotExistingNodeName(Node container, String baseName) throws RepositoryException
688    {
689        String name = baseName;
690        
691        int index = 2;
692        while (container.hasNode(name))
693        {
694            name = baseName + index;
695            index++;
696        }
697        
698        return name;
699    }
700    
701    /**
702     * Get the plugin root node in a site storage space.
703     * @param siteName the site name.
704     * @return the plugin root node.
705     * @throws RepositoryException if a repository error occurs.
706     */
707    protected Node _getPluginNode(String siteName) throws RepositoryException
708    {
709        
710        Node pluginNode;
711        Session session = null;
712        try
713        {
714            session = _repository.login();
715            ModifiableTraversableAmetysObject rootPlugins = _siteManager.getSite(siteName).getRootPlugins();
716            Node pluginsNode = ((JCRAmetysObject) rootPlugins).getNode();
717            
718            if (pluginsNode.hasNode(PLUGIN_NODE))
719            {
720                pluginNode = pluginsNode.getNode(PLUGIN_NODE);
721            }
722            else
723            {
724                pluginNode = pluginsNode.addNode(PLUGIN_NODE);
725                pluginsNode.getSession().save();
726            }
727            
728            return pluginNode;
729        }
730        catch (PathNotFoundException e)
731        {
732            if (session != null)
733            {
734                session.logout();
735            }
736
737            throw new AmetysRepositoryException("Unable to get site plugins node for site " + siteName, e);
738        }
739        catch (RepositoryException e)
740        {
741            if (session != null)
742            {
743                session.logout();
744            }
745
746            throw new AmetysRepositoryException("An error occured while getting site plugins node for site " + siteName, e);
747        }
748    }
749    
750    /**
751     * Get the queries root node.
752     * @param siteName the name of the site
753     * @return the queries root node.
754     * @throws RepositoryException if a repository error occurs.
755     */
756    protected Node _getQueriesNode(String siteName) throws RepositoryException
757    {
758        Node node;
759        try
760        {
761            Node pluginNode = _getPluginNode(siteName);
762            
763            if (pluginNode.hasNode(QUERIES_NODE))
764            {
765                node = pluginNode.getNode(QUERIES_NODE);
766            }
767            else
768            {
769                node = pluginNode.addNode(QUERIES_NODE);
770                pluginNode.getSession().save();
771            }
772            
773            return node;
774        }
775        catch (PathNotFoundException e)
776        {
777            throw new AmetysRepositoryException("Unable to get queries node because it doesn't exist.", e);
778        }
779        catch (RepositoryException e)
780        {
781            throw new AmetysRepositoryException("An error occured while getting queries node.", e);
782        }
783    }
784    
785}