View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.openr66.protocol.http.restv2.dbhandlers;
22  
23  import com.fasterxml.jackson.databind.node.ObjectNode;
24  import io.cdap.http.HttpResponder;
25  import io.netty.handler.codec.http.DefaultHttpHeaders;
26  import io.netty.handler.codec.http.HttpHeaders;
27  import io.netty.handler.codec.http.HttpMethod;
28  import io.netty.handler.codec.http.HttpRequest;
29  import org.joda.time.DateTime;
30  import org.joda.time.Period;
31  import org.waarp.common.json.JsonHandler;
32  import org.waarp.common.utility.ParametersChecker;
33  import org.waarp.common.utility.WaarpShutdownHook;
34  import org.waarp.gateway.kernel.rest.RestConfiguration.CRUD;
35  import org.waarp.openr66.context.ErrorCode;
36  import org.waarp.openr66.dao.BusinessDAO;
37  import org.waarp.openr66.dao.DAOFactory;
38  import org.waarp.openr66.dao.Filter;
39  import org.waarp.openr66.dao.HostDAO;
40  import org.waarp.openr66.dao.RuleDAO;
41  import org.waarp.openr66.dao.TransferDAO;
42  import org.waarp.openr66.dao.exception.DAOConnectionException;
43  import org.waarp.openr66.dao.exception.DAONoDataException;
44  import org.waarp.openr66.pojo.Business;
45  import org.waarp.openr66.pojo.Host;
46  import org.waarp.openr66.pojo.Rule;
47  import org.waarp.openr66.pojo.Transfer;
48  import org.waarp.openr66.pojo.UpdatedInfo;
49  import org.waarp.openr66.protocol.http.restv2.converters.ServerStatusMaker;
50  import org.waarp.openr66.protocol.http.restv2.errors.RestError;
51  import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
52  import org.waarp.openr66.protocol.http.restv2.utils.JsonUtils;
53  import org.waarp.openr66.protocol.http.restv2.utils.RestUtils;
54  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Hosts;
55  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Rules;
56  import org.waarp.openr66.protocol.http.restv2.utils.XmlSerializable.Transfers;
57  import org.waarp.openr66.protocol.http.restv2.utils.XmlUtils;
58  import org.waarp.openr66.protocol.utils.ChannelUtils;
59  
60  import javax.ws.rs.Consumes;
61  import javax.ws.rs.DefaultValue;
62  import javax.ws.rs.GET;
63  import javax.ws.rs.InternalServerErrorException;
64  import javax.ws.rs.OPTIONS;
65  import javax.ws.rs.PUT;
66  import javax.ws.rs.Path;
67  import javax.ws.rs.PathParam;
68  import javax.ws.rs.QueryParam;
69  import java.io.File;
70  import java.util.ArrayList;
71  import java.util.HashMap;
72  import java.util.List;
73  import java.util.Map;
74  import java.util.regex.Matcher;
75  import java.util.regex.Pattern;
76  
77  import static io.netty.handler.codec.http.HttpResponseStatus.*;
78  import static java.lang.Boolean.*;
79  import static javax.ws.rs.core.HttpHeaders.*;
80  import static javax.ws.rs.core.MediaType.*;
81  import static org.waarp.common.role.RoleDefault.ROLE.*;
82  import static org.waarp.openr66.dao.database.DBTransferDAO.*;
83  import static org.waarp.openr66.protocol.configuration.Configuration.*;
84  import static org.waarp.openr66.protocol.http.rest.HttpRestR66Handler.RESTHANDLERS.*;
85  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
86  import static org.waarp.openr66.protocol.http.restv2.RestConstants.ExportConfigParams.*;
87  import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetLogsParams.*;
88  import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetStatusParams.*;
89  import static org.waarp.openr66.protocol.http.restv2.RestConstants.ImportConfigParams.*;
90  import static org.waarp.openr66.protocol.http.restv2.RestConstants.ServerCommandsURI.*;
91  import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
92  
93  /**
94   * This is the {@link AbstractRestDbHandler} handling all requests made on the
95   * server commands REST entry
96   * point.
97   */
98  
99  @Path(SERVER_HANDLER_URI)
100 public class ServerHandler extends AbstractRestDbHandler {
101 
102   /**
103    * Stores the path to the archive directory.
104    */
105   private static final String ARCH_PATH =
106       configuration.getBaseDirectory() + configuration.getArchivePath();
107 
108   /**
109    * Stores the path to the configuration directory.
110    */
111   private static final String CONFIGS_PATH =
112       configuration.getBaseDirectory() + configuration.getConfigPath();
113 
114   /**
115    * Stores a {@link Map} associating each sub-path of the handler to their
116    * respective CRUD configuration.
117    */
118   private final Map<String, Byte> serverCRUD = new HashMap<String, Byte>();
119 
120   /**
121    * Instantiates the handler with the given CRUD configuration array.
122    *
123    * @param crud An array of byte containing all the REST CRUD
124    *     configurations.
125    */
126   public ServerHandler(final byte[] crud) {
127     super((byte) 0);
128     serverCRUD.put(STATUS_URI, crud[Information.ordinal()]);
129     serverCRUD.put(DEACTIVATE_URI, crud[Server.ordinal()]);
130     serverCRUD.put(SHUTDOWN_URI, crud[Server.ordinal()]);
131     serverCRUD.put(RESTART_URI, crud[Server.ordinal()]);
132     serverCRUD.put(LOGS_URI, crud[Log.ordinal()]);
133     serverCRUD.put(CONFIG_URI, crud[Config.ordinal()]);
134   }
135 
136   /**
137    * Checks if the request can be made in consideration to the handler's CRUD
138    * configuration.
139    *
140    * @param request the HttpRequest made to the handler
141    *
142    * @return {@code true} if the request is valid, {@code false} if the CRUD
143    *     configuration does not allow this
144    *     request
145    */
146   @Override
147   public boolean checkCRUD(final HttpRequest request) {
148     if (request.method().equals(HttpMethod.OPTIONS)) {
149       return true;
150     }
151 
152     final Pattern pattern =
153         Pattern.compile('(' + SERVER_HANDLER_URI + ")(\\w+)(\\?.+)?");
154     final Matcher matcher = pattern.matcher(request.uri());
155     final HttpMethod method = request.method();
156 
157     final Byte crud;
158     if (!matcher.find()) {
159       crud = 0;
160     } else {
161       final String subPath = matcher.group(2);
162       crud = serverCRUD.get(subPath);
163     }
164 
165     if (crud == null) {
166       return false;
167     } else if (method.equals(HttpMethod.GET)) {
168       return CRUD.READ.isValid(crud);
169     } else if (method.equals(HttpMethod.POST)) {
170       return CRUD.CREATE.isValid(crud);
171     } else if (method.equals(HttpMethod.DELETE)) {
172       return CRUD.DELETE.isValid(crud);
173     } else if (method.equals(HttpMethod.PUT)) {
174       return CRUD.UPDATE.isValid(crud);
175     } else {
176       return false;
177     }
178   }
179 
180   /**
181    * Get the general status of the server.
182    *
183    * @param request the HttpRequest made on the resource
184    * @param responder the HttpResponder which sends the reply to the
185    *     request
186    */
187   @Path(STATUS_URI)
188   @GET
189   @Consumes(WILDCARD)
190   @RequiredRole(READONLY)
191   public final void getStatus(final HttpRequest request,
192                               final HttpResponder responder,
193                               @QueryParam(PERIOD) @DefaultValue("P1DT0H0M0S")
194                               final String periodStr) {
195     checkSanity(periodStr);
196     try {
197       final Period period = Period.parse(periodStr);
198       final ObjectNode status = ServerStatusMaker.exportAsJson(period);
199       final String responseText = JsonUtils.nodeToString(status);
200       responder.sendJson(OK, responseText);
201     } catch (final IllegalArgumentException e) {
202       throw new RestErrorException(ILLEGAL_PARAMETER_VALUE(PERIOD, periodStr));
203     } catch (final UnsupportedOperationException e) {
204       throw new RestErrorException(ILLEGAL_PARAMETER_VALUE(PERIOD, periodStr));
205     }
206   }
207 
208   /**
209    * Method called to get a list of all allowed HTTP methods on the
210    * '/server/status' entry point. The HTTP
211    * methods are sent as an array in the reply's headers.
212    *
213    * @param request the HttpRequest made on the resource
214    * @param responder the HttpResponder which sends the reply to the
215    *     request.
216    */
217   @Path(STATUS_URI)
218   @OPTIONS
219   @Consumes(WILDCARD)
220   @RequiredRole(NOACCESS)
221   public final void status_options(final HttpRequest request,
222                                    final HttpResponder responder) {
223     final HttpHeaders allow = new DefaultHttpHeaders();
224     final List<HttpMethod> options = new ArrayList<HttpMethod>();
225     options.add(HttpMethod.GET);
226     options.add(HttpMethod.OPTIONS);
227     allow.add(ALLOW, options);
228     responder.sendStatus(OK, allow);
229   }
230 
231   /**
232    * Deactivates the server so that it doesn't accept any new transfer
233    * request.
234    *
235    * @param request the HttpRequest made on the resource
236    * @param responder the HttpResponder which sends the reply to the
237    *     request
238    */
239   @Path(DEACTIVATE_URI)
240   @PUT
241   @Consumes(WILDCARD)
242   @RequiredRole(FULLADMIN)
243   public final void deactivate(final HttpRequest request,
244                                final HttpResponder responder) {
245     HostDAO hostDAO = null;
246     try {
247       hostDAO = DAO_FACTORY.getHostDAO(false);
248       final Host host = hostDAO.select(serverName());
249       host.setActive(!host.isActive());
250       hostDAO.update(host);
251 
252       final DefaultHttpHeaders headers = new DefaultHttpHeaders();
253       headers.add("active", host.isActive());
254       responder.sendStatus(NO_CONTENT, headers);
255     } catch (final DAOConnectionException e) {
256       throw new InternalServerErrorException(e);
257     } catch (final DAONoDataException e) {
258       responder.sendStatus(NOT_FOUND);
259     } finally {
260       DAOFactory.closeDAO(hostDAO);
261     }
262   }
263 
264   /**
265    * Shut down the server.
266    *
267    * @param request the HttpRequest made on the resource
268    * @param responder the HttpResponder which sends the reply to the
269    *     request
270    */
271   @Path(SHUTDOWN_URI)
272   @PUT
273   @Consumes(WILDCARD)
274   @RequiredRole(FULLADMIN)
275   public final void shutdown(final HttpRequest request,
276                              final HttpResponder responder) {
277     WaarpShutdownHook.setRestart(false);
278     ChannelUtils.startShutdown();
279     responder.sendStatus(NO_CONTENT);
280   }
281 
282   /**
283    * Method called to get a list of all allowed HTTP methods on the
284    * '/server/shutdown' entry point. The HTTP
285    * methods are sent as an array in the reply's headers.
286    *
287    * @param request the HttpRequest made on the resource
288    * @param responder the HttpResponder which sends the reply to the
289    *     request.
290    */
291   @Path(SHUTDOWN_URI)
292   @OPTIONS
293   @Consumes(WILDCARD)
294   @RequiredRole(NOACCESS)
295   public final void shutdown_options(final HttpRequest request,
296                                      final HttpResponder responder) {
297     final HttpHeaders allow = new DefaultHttpHeaders();
298     final List<HttpMethod> options = new ArrayList<HttpMethod>();
299     options.add(HttpMethod.PUT);
300     options.add(HttpMethod.OPTIONS);
301     allow.add(ALLOW, options);
302     responder.sendStatus(OK, allow);
303   }
304 
305   /**
306    * Restart the server.
307    *
308    * @param request the HttpRequest made on the resource
309    * @param responder the HttpResponder which sends the reply to the
310    *     request
311    */
312   @Path(RESTART_URI)
313   @PUT
314   @Consumes(WILDCARD)
315   @RequiredRole(FULLADMIN)
316   public final void restart(final HttpRequest request,
317                             final HttpResponder responder) {
318     WaarpShutdownHook.setRestart(true);
319     ChannelUtils.startShutdown();
320     responder.sendStatus(NO_CONTENT);
321   }
322 
323   /**
324    * Export the server logs to a file. Only the entries that satisfy the
325    * desired filters will be exported.
326    *
327    * @param request the HttpRequest made on the resource
328    * @param responder the HttpResponder which sends the reply to the
329    *     request
330    * @param purgeStr states whether to delete exported entries or not
331    * @param cleanStr states whether to fix the incoherent entries
332    * @param statusStr only transfers with this status will be
333    *     exported
334    * @param rule only transfers using this rule will be exported
335    * @param start lower bound for the date of the transfer
336    * @param stop upper bound for the date of the transfer
337    * @param startID lower bound for the transfer's ID
338    * @param stopID upper bound for the transfer's ID
339    */
340   @Path(LOGS_URI)
341   @GET
342   @Consumes(APPLICATION_FORM_URLENCODED)
343   @RequiredRole(LOGCONTROL)
344   public final void getLogs(final HttpRequest request,
345                             final HttpResponder responder,
346                             @QueryParam(PURGE) @DefaultValue("false")
347                             final String purgeStr,
348                             @QueryParam(CLEAN) @DefaultValue("false")
349                             final String cleanStr,
350                             @QueryParam(STATUS) @DefaultValue("")
351                             final String statusStr,
352                             @QueryParam(RULE_NAME) @DefaultValue("")
353                             final String rule,
354                             @QueryParam(START) @DefaultValue("")
355                             final String start,
356                             @QueryParam(STOP) @DefaultValue("")
357                             final String stop,
358                             @QueryParam(START_ID) @DefaultValue("")
359                             final String startID,
360                             @QueryParam(STOP_ID) @DefaultValue("")
361                             final String stopID,
362                             @QueryParam(REQUESTED) @DefaultValue("")
363                             final String requester) {
364     checkSanity(purgeStr, cleanStr, statusStr, rule, start, stop, startID,
365                 stopID, requester);
366     final List<RestError> errors = new ArrayList<RestError>();
367     RestUtils.getLocale(request);
368     final List<Filter> filters = new ArrayList<Filter>();
369     final String filePath =
370         ARCH_PATH + File.separator + serverName() + "_export_" +
371         DateTime.now() + ".xml";
372 
373     boolean purge = false;
374     boolean clean = false;
375     try {
376       purge = RestUtils.stringToBoolean(purgeStr);
377     } catch (final IllegalArgumentException e) {
378       errors.add(ILLEGAL_PARAMETER_VALUE(PURGE, purgeStr));
379     }
380     try {
381       clean = RestUtils.stringToBoolean(cleanStr);
382     } catch (final IllegalArgumentException e) {
383       errors.add(ILLEGAL_PARAMETER_VALUE(CLEAN, cleanStr));
384     }
385     if (ParametersChecker.isNotEmpty(start, stop)) {
386       try {
387         filters.add(new Filter(TRANSFER_START_FIELD, Filter.BETWEEN,
388                                DateTime.parse(start), DateTime.parse(stop)));
389       } catch (final IllegalArgumentException e) {
390         errors.add(ILLEGAL_PARAMETER_VALUE(START, start));
391         errors.add(ILLEGAL_PARAMETER_VALUE(STOP, stop));
392       }
393     } else if (ParametersChecker.isNotEmpty(start)) {
394       try {
395         final DateTime lowerDate = DateTime.parse(start);
396         filters.add(new Filter(TRANSFER_START_FIELD, ">=", lowerDate));
397       } catch (final IllegalArgumentException e) {
398         errors.add(ILLEGAL_PARAMETER_VALUE(START, start));
399       }
400     } else if (ParametersChecker.isNotEmpty(stop)) {
401       try {
402         final DateTime upperDate = DateTime.parse(stop);
403         filters.add(new Filter(TRANSFER_START_FIELD, "<=", upperDate));
404       } catch (final IllegalArgumentException e) {
405         errors.add(ILLEGAL_PARAMETER_VALUE(STOP, stop));
406       }
407     }
408     if (ParametersChecker.isNotEmpty(startID, stopID)) {
409       try {
410         filters.add(
411             new Filter(ID_FIELD, Filter.BETWEEN, Long.parseLong(startID),
412                        Long.parseLong(stopID)));
413       } catch (final NumberFormatException e) {
414         errors.add(ILLEGAL_PARAMETER_VALUE(START_ID, startID));
415         errors.add(ILLEGAL_PARAMETER_VALUE(STOP_ID, stopID));
416       }
417     } else if (ParametersChecker.isNotEmpty(startID)) {
418       try {
419         final Long lowerID = Long.parseLong(startID);
420         filters.add(new Filter(ID_FIELD, ">=", lowerID));
421       } catch (final NumberFormatException e) {
422         errors.add(ILLEGAL_PARAMETER_VALUE(START_ID, startID));
423       }
424     } else if (ParametersChecker.isNotEmpty(stopID)) {
425       try {
426         final Long upperID = Long.parseLong(stopID);
427         filters.add(new Filter(ID_FIELD, "<=", upperID));
428       } catch (final NumberFormatException e) {
429         errors.add(ILLEGAL_PARAMETER_VALUE(STOP_ID, stopID));
430       }
431     }
432     if (!rule.isEmpty()) {
433       filters.add(new Filter(ID_RULE_FIELD, "=", rule));
434     }
435     if (!statusStr.isEmpty()) {
436       try {
437         final UpdatedInfo status = UpdatedInfo.valueOf(statusStr);
438         filters.add(new Filter(UPDATED_INFO_FIELD, "=", status.ordinal()));
439       } catch (final IllegalArgumentException e) {
440         errors.add(ILLEGAL_PARAMETER_VALUE(STATUS, statusStr));
441       }
442     }
443 
444     if (!requester.isEmpty()) {
445       filters.add(new Filter(REQUESTER_FIELD, "=", requester));
446     }
447 
448     if (!errors.isEmpty()) {
449       throw new RestErrorException(errors);
450     }
451     TransferDAO transferDAO = null;
452     try {
453       transferDAO = DAO_FACTORY.getTransferDAO();
454       final Transfers transfers = new Transfers(transferDAO.find(filters));
455       final int exported = transfers.transfers.size();
456 
457       XmlUtils.saveObject(transfers, filePath);
458       int purged = 0;
459       if (purge) {
460         for (final Transfer transfer : transfers.transfers) {
461           transferDAO.delete(transfer);
462           ++purged;
463         }
464       }
465       // Update all UpdatedInfo to DONE
466       // where GlobalLastStep = ALLDONETASK and status = CompleteOk
467       if (clean) {
468         for (final Transfer transfer : transfers.transfers) {
469           if (transfer.getGlobalStep() == Transfer.TASKSTEP.ALLDONETASK &&
470               transfer.getInfoStatus() == ErrorCode.CompleteOk) {
471             transfer.setUpdatedInfo(UpdatedInfo.DONE);
472             transferDAO.update(transfer);
473           }
474         }
475       }
476 
477       final ObjectNode responseObject = JsonHandler.createObjectNode();
478       responseObject.put("filePath", filePath);
479       responseObject.put("exported", exported);
480       responseObject.put("purged", purged);
481       final String responseText = JsonUtils.nodeToString(responseObject);
482       responder.sendJson(OK, responseText);
483 
484     } catch (final DAOConnectionException e) {
485       throw new InternalServerErrorException(e);
486     } catch (final DAONoDataException e) {
487       responder.sendStatus(NOT_FOUND);
488     } finally {
489       DAOFactory.closeDAO(transferDAO);
490     }
491   }
492 
493   /**
494    * Exports parts of the current server configuration to multiple XML files,
495    * depending on the parameters of the
496    * request.
497    *
498    * @param request the HttpRequest made on the resource
499    * @param responder the HttpResponder which sends the reply to the
500    *     request
501    * @param hostStr states whether to export the host database or
502    *     not.
503    * @param ruleStr states whether to export the rules database or
504    *     not.
505    * @param businessStr states whether to export the host's business
506    *     or not.
507    * @param aliasStr states whether to export the host's aliases or
508    *     not.
509    * @param roleStr states whether to export the host's permission
510    *     database or not.
511    */
512   @Path(CONFIG_URI)
513   @GET
514   @Consumes(APPLICATION_FORM_URLENCODED)
515   @RequiredRole(CONFIGADMIN)
516   public final void getConfig(final HttpRequest request,
517                               final HttpResponder responder,
518                               @QueryParam(EXPORT_HOSTS) @DefaultValue("false")
519                               final String hostStr,
520                               @QueryParam(EXPORT_RULES) @DefaultValue("false")
521                               final String ruleStr, @QueryParam(EXPORT_BUSINESS)
522                               @DefaultValue("false") final String businessStr,
523                               @QueryParam(EXPORT_ALIASES) @DefaultValue("false")
524                               final String aliasStr,
525                               @QueryParam(EXPORT_ROLES) @DefaultValue("false")
526                               final String roleStr) {
527     checkSanity(hostStr, ruleStr, businessStr, aliasStr, roleStr);
528     final List<RestError> errors = new ArrayList<RestError>();
529 
530     boolean host = false;
531     boolean rule = false;
532     boolean business = false;
533     boolean alias = false;
534     boolean role = false;
535 
536     try {
537       host = RestUtils.stringToBoolean(hostStr);
538     } catch (final IllegalArgumentException e) {
539       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_HOSTS, hostStr));
540     }
541     try {
542       rule = RestUtils.stringToBoolean(ruleStr);
543     } catch (final IllegalArgumentException e) {
544       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_RULES, ruleStr));
545     }
546     try {
547       business = RestUtils.stringToBoolean(businessStr);
548     } catch (final IllegalArgumentException e) {
549       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_BUSINESS, businessStr));
550     }
551     try {
552       alias = RestUtils.stringToBoolean(aliasStr);
553     } catch (final IllegalArgumentException e) {
554       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_ALIASES, aliasStr));
555     }
556     try {
557       role = RestUtils.stringToBoolean(roleStr);
558     } catch (final IllegalArgumentException e) {
559       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_ROLES, roleStr));
560     }
561     if (!errors.isEmpty()) {
562       throw new RestErrorException(errors);
563     }
564 
565     final String hostsFilePath =
566         CONFIGS_PATH + File.separator + serverName() + "_hosts.xml";
567     final String rulesFilePath =
568         CONFIGS_PATH + File.separator + serverName() + "_rules.xml";
569     final String businessFilePath =
570         CONFIGS_PATH + File.separator + serverName() + "_business.xml";
571     final String aliasFilePath =
572         CONFIGS_PATH + File.separator + serverName() + "_aliases.xml";
573     final String rolesFilePath =
574         CONFIGS_PATH + File.separator + serverName() + "_roles.xml";
575 
576     final ObjectNode responseObject = JsonHandler.createObjectNode();
577 
578     HostDAO hostDAO = null;
579     RuleDAO ruleDAO = null;
580     BusinessDAO businessDAO = null;
581     try {
582       if (host) {
583         hostDAO = DAO_FACTORY.getHostDAO(false);
584         final List<Host> hostList = hostDAO.getAll();
585 
586         final Hosts hosts = new Hosts(hostList);
587 
588         XmlUtils.saveObject(hosts, hostsFilePath);
589         responseObject.put("fileHost", hostsFilePath);
590       }
591       if (rule) {
592         ruleDAO = DAO_FACTORY.getRuleDAO(false);
593         final Rules rules = new Rules(ruleDAO.getAll());
594 
595         XmlUtils.saveObject(rules, rulesFilePath);
596         responseObject.put("fileRule", rulesFilePath);
597       }
598       businessDAO = DAO_FACTORY.getBusinessDAO(true);
599       final Business businessEntry = businessDAO.select(serverName());
600       if (business) {
601         final String businessXML = businessEntry.getBusiness();
602 
603         XmlUtils.saveXML(businessXML, businessFilePath);
604         responseObject.put("fileBusiness", businessFilePath);
605       }
606       if (alias) {
607         final String aliasXML = businessEntry.getAliases();
608 
609         XmlUtils.saveXML(aliasXML, aliasFilePath);
610         responseObject.put("fileAlias", aliasFilePath);
611       }
612       if (role) {
613         final String rolesXML = businessEntry.getRoles();
614 
615         XmlUtils.saveXML(rolesXML, rolesFilePath);
616         responseObject.put("fileRoles", rolesFilePath);
617       }
618 
619       final String responseText = JsonUtils.nodeToString(responseObject);
620       responder.sendJson(OK, responseText);
621 
622     } catch (final DAOConnectionException e) {
623       throw new InternalServerErrorException(e);
624     } catch (final DAONoDataException e) {
625       responder.sendStatus(NOT_FOUND);
626     } finally {
627       DAOFactory.closeDAO(hostDAO);
628       DAOFactory.closeDAO(ruleDAO);
629       DAOFactory.closeDAO(businessDAO);
630     }
631   }
632 
633   /**
634    * Imports different parts of the server configuration from the XML files
635    * given as parameters of the request.
636    * These imported values will replace those already present in the
637    * database.
638    *
639    * @param request the HttpRequest made on the resource
640    * @param responder the HttpResponder which sends the reply to the
641    *     request
642    * @param purgeHostStr states if a new host database should be
643    *     imported.
644    * @param purgeRuleStr states if a new transfer rule database
645    *     should be imported
646    * @param purgeBusinessStr states if a new business database should
647    *     be imported
648    * @param purgeAliasStr states if a new alias database should be
649    *     imported
650    * @param purgeRoleStr states if a new role database should be
651    *     imported
652    * @param hostFile path to the XML file containing the host database
653    *     to import
654    * @param ruleFile path to the XML file containing the rule database
655    *     to import
656    * @param businessFile path to the XML file containing the business
657    *     database to import
658    * @param aliasFile path to the XML file containing the alias
659    *     database to import
660    * @param roleFile path to the XML file containing the role database
661    *     to import
662    */
663   @Path(CONFIG_URI)
664   @PUT
665   @Consumes(APPLICATION_FORM_URLENCODED)
666   @RequiredRole(CONFIGADMIN)
667   public final void setConfig(final HttpRequest request,
668                               final HttpResponder responder,
669                               @QueryParam(PURGE_HOST) @DefaultValue("false")
670                               final String purgeHostStr,
671                               @QueryParam(PURGE_RULE) @DefaultValue("false")
672                               final String purgeRuleStr,
673                               @QueryParam(PURGE_BUSINESS) @DefaultValue("false")
674                               final String purgeBusinessStr,
675                               @QueryParam(PURGE_ALIASES) @DefaultValue("false")
676                               final String purgeAliasStr,
677                               @QueryParam(PURGE_ROLES) @DefaultValue("false")
678                               final String purgeRoleStr,
679                               @QueryParam(HOST_FILE) @DefaultValue("")
680                               final String hostFile,
681                               @QueryParam(RULE_FILE) @DefaultValue("")
682                               final String ruleFile,
683                               @QueryParam(BUSINESS_FILE) @DefaultValue("")
684                               final String businessFile,
685                               @QueryParam(ALIAS_FILE) @DefaultValue("")
686                               final String aliasFile,
687                               @QueryParam(ROLE_FILE) @DefaultValue("")
688                               final String roleFile) {
689     checkSanity(purgeHostStr, purgeRuleStr, purgeBusinessStr, purgeAliasStr,
690                 purgeRoleStr, hostFile, ruleFile, businessFile, aliasFile,
691                 ruleFile);
692     final List<RestError> errors = new ArrayList<RestError>();
693     RestUtils.getLocale(request);
694 
695     boolean purgeHost = false;
696     boolean purgeRule = false;
697     boolean purgeBusiness = false;
698     boolean purgeAlias = false;
699     boolean purgeRole = false;
700 
701     try {
702       purgeHost = RestUtils.stringToBoolean(purgeHostStr);
703     } catch (final IllegalArgumentException e) {
704       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_HOSTS, purgeHostStr));
705     }
706     try {
707       purgeRule = RestUtils.stringToBoolean(purgeRuleStr);
708     } catch (final IllegalArgumentException e) {
709       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_RULES, purgeRuleStr));
710     }
711     try {
712       purgeBusiness = RestUtils.stringToBoolean(purgeBusinessStr);
713     } catch (final IllegalArgumentException e) {
714       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_BUSINESS, purgeBusinessStr));
715     }
716     try {
717       purgeAlias = RestUtils.stringToBoolean(purgeAliasStr);
718     } catch (final IllegalArgumentException e) {
719       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_ALIASES, purgeAliasStr));
720     }
721     try {
722       purgeRole = RestUtils.stringToBoolean(purgeRoleStr);
723     } catch (final IllegalArgumentException e) {
724       errors.add(ILLEGAL_PARAMETER_VALUE(EXPORT_ROLES, purgeRoleStr));
725     }
726     if (!errors.isEmpty()) {
727       throw new RestErrorException(errors);
728     }
729 
730     HostDAO hostDAO = null;
731     RuleDAO ruleDAO = null;
732     BusinessDAO businessDAO = null;
733 
734     final ObjectNode responseObject = JsonHandler.createObjectNode();
735 
736     try {
737       hostDAO = DAO_FACTORY.getHostDAO(false);
738       final Hosts hosts = XmlUtils.loadObject(hostFile, Hosts.class);
739 
740       // if a purge is requested, we can add the new entries without
741       // checking with 'exist' to gain performance
742       if (purgeHost) {
743         hostDAO.deleteAll();
744         for (final Host host : hosts.hosts) {
745           hostDAO.insert(host);
746         }
747       } else {
748         for (final Host host : hosts.hosts) {
749           if (hostDAO.exist(host.getHostid())) {
750             hostDAO.update(host);
751           } else {
752             hostDAO.insert(host);
753           }
754         }
755       }
756       responseObject.put("purgedHost", TRUE.toString());
757 
758       ruleDAO = DAO_FACTORY.getRuleDAO(false);
759 
760       final Rules rules = XmlUtils.loadObject(ruleFile, Rules.class);
761 
762       if (purgeRule) {
763         ruleDAO.deleteAll();
764         for (final Rule rule : rules.rules) {
765           ruleDAO.insert(rule);
766         }
767       } else {
768         for (final Rule rule : rules.rules) {
769           if (ruleDAO.exist(rule.getName())) {
770             ruleDAO.update(rule);
771           } else {
772             ruleDAO.insert(rule);
773           }
774         }
775       }
776 
777       responseObject.put("purgedRule", TRUE.toString());
778 
779       businessDAO = DAO_FACTORY.getBusinessDAO(false);
780       if (purgeBusiness) {
781         final Business business = businessDAO.select(serverName());
782 
783         final String new_business = XmlUtils.loadXML(businessFile);
784         business.setBusiness(new_business);
785         businessDAO.update(business);
786         responseObject.put("purgedBusiness", TRUE.toString());
787       }
788       if (purgeAlias) {
789         final Business business = businessDAO.select(serverName());
790         business.setAliases(XmlUtils.loadXML(aliasFile));
791         businessDAO.update(business);
792         responseObject.put("purgedAlias", TRUE.toString());
793       }
794       if (purgeRole) {
795         final Business business = businessDAO.select(serverName());
796         business.setRoles(XmlUtils.loadXML(roleFile));
797         businessDAO.update(business);
798         responseObject.put("purgedRoles", TRUE.toString());
799       }
800 
801       if (errors.isEmpty()) {
802         responder.sendJson(OK, JsonUtils.nodeToString(responseObject));
803       } else {
804         throw new RestErrorException(errors);
805       }
806 
807     } catch (final DAOConnectionException e) {
808       throw new InternalServerErrorException(e);
809     } catch (final DAONoDataException e) {
810       responder.sendStatus(NOT_FOUND);
811     } finally {
812       DAOFactory.closeDAO(hostDAO);
813       DAOFactory.closeDAO(ruleDAO);
814       DAOFactory.closeDAO(businessDAO);
815     }
816   }
817 
818   /**
819    * Method called to get a list of all allowed HTTP methods on the '/server'
820    * entry point. The HTTP methods are
821    * sent as an array in the reply's headers.
822    *
823    * @param request the HttpRequest made on the resource
824    * @param responder the HttpResponder which sends the reply to the
825    *     request.
826    */
827   @OPTIONS
828   @Consumes(WILDCARD)
829   @RequiredRole(NOACCESS)
830   public final void options(final HttpRequest request,
831                             final HttpResponder responder) {
832     final HttpHeaders allow = new DefaultHttpHeaders();
833     allow.add(ALLOW, HttpMethod.OPTIONS);
834     responder.sendStatus(OK, allow);
835   }
836 
837   /**
838    * Method called to get a list of all allowed HTTP methods on all sub entry
839    * points of the '/server' entry
840    * point. The HTTP methods are sent as an array in the reply's headers.
841    *
842    * @param request the HttpRequest made on the resource
843    * @param responder the HttpResponder which sends the reply to the
844    *     request.
845    */
846   @Path("{ep}")
847   @OPTIONS
848   @Consumes(WILDCARD)
849   @RequiredRole(NOACCESS)
850   public final void command_options(final HttpRequest request,
851                                     final HttpResponder responder,
852                                     @PathParam("ep") final String ep) {
853     checkSanity(ep);
854     final HttpHeaders allow = new DefaultHttpHeaders();
855     final List<HttpMethod> options = new ArrayList<HttpMethod>();
856 
857     if (ep.equals(STATUS_URI)) {
858       options.add(HttpMethod.GET);
859     } else if (ep.equals(DEACTIVATE_URI)) {
860       options.add(HttpMethod.PUT);
861     } else if (ep.equals(SHUTDOWN_URI)) {
862       options.add(HttpMethod.PUT);
863     } else if (ep.equals(RESTART_URI)) {
864       options.add(HttpMethod.PUT);
865     } else if (ep.equals(LOGS_URI)) {
866       options.add(HttpMethod.GET);
867     } else if (ep.equals(CONFIG_URI)) {
868       options.add(HttpMethod.PUT);
869     } else {
870       responder.sendStatus(FOUND);
871       return;
872     }
873     options.add(HttpMethod.OPTIONS);
874     allow.add(ALLOW, options);
875     responder.sendStatus(OK, allow);
876   }
877 }