docs: public boolean restoreBackupData(InputStream streamData) {
          // convert stream to string
          java.util.Scanner s = new java.util.Scanner(streamData).useDelimiter("\\A");
          String data = s.hasNext() ? s.next() : "";
  
          // parse JSON
          ObjectMapper mapper = new ObjectMapper();
          Backup backupData = null;
          try {
              backupData = mapper.readValue(data, Backup.class);
          } catch (Exception e) {
              logger.error("Could not parse input data: {}, {}", e.getClass(), e.getMessage());
              return false;
          }
  
          // TODO: validate json against a schema for safety
  
          // GROUPS
          try {
              logger.info("Number of groups: {}", backupData.getGroups().size());
  
              for (Group group : backupData.getGroups()) {
                  // determine if group already exists.. if not then add it
                  Integer groupId = PathOverrideService.getInstance().getGroupIdFromName(group.getName());
                  if (groupId == null) {
                      groupId = PathOverrideService.getInstance().addGroup(group.getName());
                  }
  
                  // get all methods from the group.. we are going to remove ones that don't exist in the new configuration
                  List<Method> originalMethods = EditService.getInstance().getMethodsFromGroupId(groupId, null);
  
                  for (Method originalMethod : originalMethods) {
                      Boolean matchInImportGroup = false;
  
                      int importCount = 0;
                      for (Method importMethod : group.getMethods()) {
                          if (originalMethod.getClassName().equals(importMethod.getClassName()) &&
                              originalMethod.getMethodName().equals(importMethod.getMethodName())) {
                              matchInImportGroup = true;
                              break;
                          }
                          importCount++;
                      }
  
                      if (!matchInImportGroup) {
                          // remove it from current database since it is a delta to the current import
                          PathOverrideService.getInstance().removeOverride(originalMethod.getId());
                      } else {
                          // remove from import list since it already exists
                          group.getMethods().remove(importCount);
                      }
                  }
  
                  // add methods to groups
                  for (Method method : group.getMethods()) {
                      PathOverrideService.getInstance().createOverride(groupId, method.getMethodName(), method.getClassName());
                  }
              }
          } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
  
          // PROFILES
          try {
              logger.info("Number of profiles: {}", backupData.getProfiles().size());
  
              // remove all servers
              // don't care about deltas here.. we'll just recreate them all
              // removed default servers (belong to group id=0)
              ServerRedirectService.getInstance().deleteServerGroup(0);
  
              for (com.groupon.odo.proxylib.models.backup.Profile profile : backupData.getProfiles()) {
                  // see if a profile with this name already exists
                  Integer profileId = ProfileService.getInstance().getIdFromName(profile.getName());
                  com.groupon.odo.proxylib.models.Profile newProfile;
                  if (profileId == null) {
                      // create new profile
                      newProfile = ProfileService.getInstance().add(profile.getName());
                  } else {
                      // get the existing profile
                      newProfile = ProfileService.getInstance().findProfile(profileId);
                  }
  
                  // add new servers
                  if (profile.getServers() != null) {
                      for (ServerRedirect server : profile.getServers()) {
                          ServerRedirectService.getInstance().addServerRedirect(server.getRegion(), server.getSrcUrl(), server.getDestUrl(), server.getHostHeader(), newProfile.getId(), 0);
                      }
                  }
  
                  // remove all server groups
                  for (ServerGroup group : ServerRedirectService.getInstance().tableServerGroups(newProfile.getId())) {
                      ServerRedirectService.getInstance().deleteServerGroup(group.getId());
                  }
  
                  // add new server groups
                  if (profile.getServerGroups() != null) {
                      for (ServerGroup group : profile.getServerGroups()) {
                          int groupId = ServerRedirectService.getInstance().addServerGroup(group.getName(), newProfile.getId());
                          for (ServerRedirect server : group.getServers()) {
                              ServerRedirectService.getInstance().addServerRedirect(server.getRegion(), server.getSrcUrl(), server.getDestUrl(), server.getHostHeader(), newProfile.getId(), groupId);
                          }
                      }
                  }
  
                  // remove all paths
                  // don't care about deltas here.. we'll just recreate them all
                  for (EndpointOverride path : PathOverrideService.getInstance().getPaths(newProfile.getId(), Constants.PROFILE_CLIENT_DEFAULT_ID, null)) {
                      PathOverrideService.getInstance().removePath(path.getPathId());
                  }
  
                  // add new paths
                  if (profile.getPaths() != null) {
                      for (EndpointOverride path : profile.getPaths()) {
                          int pathId = PathOverrideService.getInstance().addPathnameToProfile(newProfile.getId(), path.getPathName(), path.getPath());
  
                          PathOverrideService.getInstance().setContentType(pathId, path.getContentType());
                          PathOverrideService.getInstance().setRequestType(pathId, path.getRequestType());
                          PathOverrideService.getInstance().setGlobal(pathId, path.getGlobal());
  
                          // add groups to path
                          for (String groupName : path.getGroupNames()) {
                              int groupId = PathOverrideService.getInstance().getGroupIdFromName(groupName);
                              PathOverrideService.getInstance().AddGroupByNumber(newProfile.getId(), pathId, groupId);
                          }
                      }
                  }
  
                  // set active
                  ClientService.getInstance().updateActive(newProfile.getId(), Constants.PROFILE_CLIENT_DEFAULT_ID, profile.getActive());
              }
          } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
  
          // SCRIPTS
          try {
              // delete all scripts
              for (Script script : ScriptService.getInstance().getScripts()) {
                  ScriptService.getInstance().removeScript(script.getId());
              }
  
              // add scripts
              for (Script script : backupData.getScripts()) {
                  ScriptService.getInstance().addScript(script.getName(), script.getScript());
              }
          } catch (Exception e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
  
          // tell http/https proxies to reload plugins
          try {
              org.apache.http.conn.ssl.SSLSocketFactory sslsf = new org.apache.http.conn.ssl.SSLSocketFactory(new TrustStrategy() {
                  @Override
                  public boolean isTrusted(
                      final X509Certificate[] chain, String authType) throws CertificateException {
                      // ignore SSL cert issues
                      return true;
                  }
              });
              HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
              sslsf.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier);
              for (String connectstring : getConnectorStrings("https_proxy")) {
                  HttpGet request = new HttpGet(connectstring + "/proxy/reload");
  
                  HttpClient httpClient = new org.apache.http.impl.client.DefaultHttpClient();
                  String[] parts = connectstring.split(":");
                  httpClient.getConnectionManager().getSchemeRegistry().register(new org.apache.http.conn.scheme.Scheme("https", Integer.parseInt(parts[parts.length - 1]), sslsf));
                  HttpResponse response = httpClient.execute(request);
              }
          } catch (Exception e) {
              e.printStackTrace();
              logger.info("Exception caught during proxy reload.  Things may be in an inconsistent state.");
          }
  
          // restart plugin service for this process
          PluginManager.destroy();
  
          return true;
      }