public class AuthDaoImpl extends AbstractJooqDao implements AuthDao {

  @Inject GenericResourceDao resourceDao;
  @Inject ObjectManager objectManager;
  @Inject ObjectProcessManager objectProcessManager;
  @Inject LockManager lockManager;
  private DynamicStringListProperty SUPPORTED_TYPES =
      ArchaiusUtil.getList("account.by.key.credential.types");

  @Override
  public Account getAdminAccount() {
    return create()
        .selectFrom(ACCOUNT)
        .where(
            ACCOUNT
                .STATE
                .eq(CommonStatesConstants.ACTIVE)
                .and(ACCOUNT.KIND.eq(AccountConstants.ADMIN_KIND)))
        .orderBy(ACCOUNT.ID.asc())
        .limit(1)
        .fetchOne();
  }

  @Override
  public List<Account> searchUsers(String username) {
    return create()
        .select(ACCOUNT.fields())
        .from(ACCOUNT)
        .join(CREDENTIAL)
        .on(CREDENTIAL.ACCOUNT_ID.eq(ACCOUNT.ID))
        .where(
            ACCOUNT
                .STATE
                .eq(CommonStatesConstants.ACTIVE)
                .and(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE))
                .and(CREDENTIAL.PUBLIC_VALUE.contains(username))
                .and(CREDENTIAL.KIND.eq(CredentialConstants.KIND_PASSWORD)))
        .orderBy(ACCOUNT.ID.asc())
        .fetchInto(Account.class);
  }

  @Override
  public Account getByUsername(String username) {
    try {
      return create()
          .select(ACCOUNT.fields())
          .from(ACCOUNT)
          .join(CREDENTIAL)
          .on(CREDENTIAL.ACCOUNT_ID.eq(ACCOUNT.ID))
          .where(
              ACCOUNT
                  .STATE
                  .eq(CommonStatesConstants.ACTIVE)
                  .and(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE))
                  .and(CREDENTIAL.PUBLIC_VALUE.eq(username)))
          .and(CREDENTIAL.KIND.eq(CredentialConstants.KIND_PASSWORD))
          .fetchOneInto(AccountRecord.class);
    } catch (InvalidResultException e) {
      throw new ClientVisibleException(ResponseCodes.CONFLICT, "MultipleOfUsername");
    }
  }

  @Override
  public Account getAccountByLogin(String publicValue, String secretValue) {
    Credential credential =
        create()
            .selectFrom(CREDENTIAL)
            .where(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE))
            .and(
                CREDENTIAL
                    .PUBLIC_VALUE
                    .eq(publicValue)
                    .and(CREDENTIAL.KIND.equalIgnoreCase(CredentialConstants.KIND_PASSWORD)))
            .fetchOne();
    if (credential == null) {
      return null;
    }
    boolean secretIsCorrect =
        ApiContext.getContext()
            .getTransformationService()
            .compare(secretValue, credential.getSecretValue());
    if (secretIsCorrect) {
      return create()
          .selectFrom(ACCOUNT)
          .where(
              ACCOUNT
                  .ID
                  .eq(credential.getAccountId())
                  .and(ACCOUNT.STATE.eq(CommonStatesConstants.ACTIVE)))
          .fetchOneInto(AccountRecord.class);
    } else {
      return null;
    }
  }

  @Override
  public Account getAccountById(Long id) {
    return create()
        .selectFrom(ACCOUNT)
        .where(
            ACCOUNT
                .ID
                .eq(id)
                .and(ACCOUNT.STATE.ne(CommonStatesConstants.PURGED))
                .and(ACCOUNT.REMOVED.isNull()))
        .fetchOne();
  }

  @Override
  public Account getAccountByKeys(String access, String secretKey) {
    try {
      Credential credential =
          create()
              .selectFrom(CREDENTIAL)
              .where(CREDENTIAL.STATE.eq(CommonStatesConstants.ACTIVE))
              .and(CREDENTIAL.PUBLIC_VALUE.eq(access))
              .and(CREDENTIAL.KIND.in(SUPPORTED_TYPES.get()))
              .fetchOne();
      if (credential == null) {
        return null;
      }
      boolean secretIsCorrect =
          ApiContext.getContext()
              .getTransformationService()
              .compare(secretKey, credential.getSecretValue());
      if (secretIsCorrect) {
        return create()
            .selectFrom(ACCOUNT)
            .where(
                ACCOUNT
                    .ID
                    .eq(credential.getAccountId())
                    .and(ACCOUNT.STATE.eq(CommonStatesConstants.ACTIVE)))
            .fetchOneInto(AccountRecord.class);
      } else {
        return null;
      }
    } catch (InvalidResultException e) {
      throw new ClientVisibleException(ResponseCodes.CONFLICT, "MultipleKeys");
    }
  }

  @Override
  public Account getAccountByExternalId(String externalId, String externalType) {
    return create()
        .selectFrom(ACCOUNT)
        .where(
            ACCOUNT
                .EXTERNAL_ID
                .eq(externalId)
                .and(ACCOUNT.EXTERNAL_ID_TYPE.eq(externalType))
                .and(ACCOUNT.STATE.ne("purged")))
        .orderBy(ACCOUNT.ID.asc())
        .fetchOne();
  }

  @Override
  public Account createAccount(String name, String kind, String externalId, String externalType) {
    Account account = getAccountByExternalId(externalId, externalType);
    if (account != null) {
      throw new ClientVisibleException(ResponseCodes.UNAUTHORIZED);
    }
    Map<Object, Object> properties = new HashMap<>();
    if (StringUtils.isNotEmpty(name)) {
      properties.put(ACCOUNT.NAME, name);
    }
    if (StringUtils.isNotEmpty(kind)) {
      properties.put(ACCOUNT.KIND, kind);
    }
    if (StringUtils.isNotEmpty(externalId)) {
      properties.put(ACCOUNT.EXTERNAL_ID, externalId);
    }
    if (StringUtils.isNotEmpty(externalType)) {
      properties.put(ACCOUNT.EXTERNAL_ID_TYPE, externalType);
    }
    return resourceDao.createAndSchedule(
        Account.class, objectManager.convertToPropertiesFor(Account.class, properties));
  }

  @Override
  public Identity getIdentity(Long id) {
    Account account = getAccountById(id);
    if (account == null || account.getKind().equalsIgnoreCase(ProjectConstants.TYPE)) {
      return null;
    }
    Credential credential =
        create()
            .selectFrom(CREDENTIAL)
            .where(
                CREDENTIAL
                    .KIND
                    .equalIgnoreCase(CredentialConstants.KIND_PASSWORD)
                    .and(CREDENTIAL.ACCOUNT_ID.eq(id))
                    .and(CREDENTIAL.STATE.equalIgnoreCase(CommonStatesConstants.ACTIVE)))
            .fetchAny();
    String accountId =
        (String)
            ApiContext.getContext()
                .getIdFormatter()
                .formatId(objectManager.getType(Account.class), account.getId());
    return new Identity(
        ProjectConstants.RANCHER_ID,
        accountId,
        account.getName(),
        null,
        null,
        credential == null ? null : credential.getPublicValue());
  }

  @Override
  public Account createProject(String name, String description) {
    Map<Object, Object> properties = new HashMap<>();
    if (name != null) {
      properties.put(ACCOUNT.NAME, name);
    }
    if (description != null) {
      properties.put(ACCOUNT.DESCRIPTION, description);
    }
    properties.put(ACCOUNT.KIND, ProjectConstants.TYPE);
    return resourceDao.createAndSchedule(
        Account.class, objectManager.convertToPropertiesFor(Account.class, properties));
  }

  @Override
  public Account getAccountByUuid(String uuid) {
    return create()
        .selectFrom(ACCOUNT)
        .where(ACCOUNT.UUID.eq(uuid).and(ACCOUNT.STATE.eq(CommonStatesConstants.ACTIVE)))
        .orderBy(ACCOUNT.ID.asc())
        .limit(1)
        .fetchOne();
  }

  @Override
  public void updateAccount(
      Account account, String name, String kind, String externalId, String externalType) {
    Map<TableField<AccountRecord, String>, String> properties = new HashMap<>();
    if (StringUtils.isNotEmpty(name)) {
      properties.put(ACCOUNT.NAME, name);
    }
    if (StringUtils.isNotEmpty(kind)) {
      properties.put(ACCOUNT.KIND, kind);
    }
    if (StringUtils.isNotEmpty(externalId)) {
      properties.put(ACCOUNT.EXTERNAL_ID, externalId);
    }
    if (StringUtils.isNotEmpty(externalType)) {
      properties.put(ACCOUNT.EXTERNAL_ID_TYPE, externalType);
    }
    int updateCount =
        create().update(ACCOUNT).set(properties).where(ACCOUNT.ID.eq(account.getId())).execute();

    if (1 != updateCount) {
      throw new RuntimeException("UpdateAccount failed.");
    }
  }

  @Override
  public List<Account> getAccessibleProjects(
      Set<Identity> identities, boolean isAdmin, Long usingAccount) {
    List<Account> projects = new ArrayList<>();
    if (identities == null) {
      return projects;
    }
    if (isAdmin) {
      projects.addAll(
          create()
              .selectFrom(ACCOUNT)
              .where(ACCOUNT.KIND.eq(ProjectConstants.TYPE).and(ACCOUNT.REMOVED.isNull()))
              .orderBy(ACCOUNT.ID.asc())
              .fetch());
      return projects;
    }

    if (usingAccount != null) {
      Account project = getAccountById(usingAccount);
      if (project != null && project.getKind().equalsIgnoreCase(ProjectConstants.TYPE)) {
        projects.add(project);
        return projects;
      }
    }
    // DSL.falseCondition is created so that we can dynamically build a or
    // Condition without caring what the external Ids are and still make one
    // Database call.
    Condition allMembers = DSL.falseCondition();
    for (Identity id : identities) {
      allMembers =
          allMembers.or(
              PROJECT_MEMBER
                  .EXTERNAL_ID
                  .eq(id.getExternalId())
                  .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(id.getExternalIdType()))
                  .and(PROJECT_MEMBER.REMOVED.isNull())
                  .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE)));
    }
    SelectQuery<Record> query = create().selectQuery();
    query.addFrom(ACCOUNT);
    query.addJoin(PROJECT_MEMBER, PROJECT_MEMBER.PROJECT_ID.equal(ACCOUNT.ID));
    query.addConditions(allMembers);
    query.setDistinct(true);
    projects.addAll(query.fetchInto(ACCOUNT));
    Map<Long, Account> returnProjects = new HashMap<>();
    for (Account project : projects) {
      returnProjects.put(project.getId(), project);
    }
    projects = new ArrayList<>();
    projects.addAll(returnProjects.values());
    return projects;
  }

  public List<? extends ProjectMember> getActiveProjectMembers(long id) {
    return create()
        .selectFrom(PROJECT_MEMBER)
        .where(PROJECT_MEMBER.PROJECT_ID.eq(id))
        .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE))
        .and(PROJECT_MEMBER.REMOVED.isNull())
        .orderBy(PROJECT_MEMBER.ID.asc())
        .fetch();
  }

  public List<? extends ProjectMember> getProjectMembersByIdentity(
      long projectId, Set<Identity> identities) {
    Condition allMembers = DSL.falseCondition();
    for (Identity identity : identities) {
      allMembers =
          allMembers.or(
              PROJECT_MEMBER
                  .EXTERNAL_ID
                  .eq(identity.getExternalId())
                  .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(identity.getExternalIdType()))
                  .and(PROJECT_MEMBER.REMOVED.isNull())
                  .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE))
                  .and(PROJECT_MEMBER.PROJECT_ID.eq(projectId)));
    }
    SelectQuery<Record> query = create().selectQuery();
    query.addFrom(PROJECT_MEMBER);
    query.addConditions(allMembers);
    query.setDistinct(true);
    return query.fetchInto(PROJECT_MEMBER);
  }

  @Override
  public ProjectMember getProjectMember(long id) {
    return create().selectFrom(PROJECT_MEMBER).where(PROJECT_MEMBER.ID.eq(id)).fetchOne();
  }

  @Override
  public boolean hasAccessToProject(
      long projectId, Long usingAccount, boolean isAdmin, Set<Identity> identities) {
    return isProjectMember(projectId, usingAccount, isAdmin, identities);
  }

  @Override
  public boolean isProjectOwner(
      long projectId, Long usingAccount, boolean isAdmin, Set<Identity> identities) {
    if (identities == null) {
      return false;
    }
    if (isAdmin) {
      return true;
    }
    if (usingAccount != null && usingAccount.equals(projectId)) {
      return false;
    }
    Set<ProjectMemberRecord> projectMembers = new HashSet<>();
    Condition allMembers = DSL.falseCondition();
    for (Identity id : identities) {
      allMembers =
          allMembers.or(
              PROJECT_MEMBER
                  .EXTERNAL_ID
                  .eq(id.getExternalId())
                  .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(id.getExternalIdType()))
                  .and(PROJECT_MEMBER.ROLE.eq(ProjectConstants.OWNER))
                  .and(PROJECT_MEMBER.PROJECT_ID.eq(projectId))
                  .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE))
                  .and(PROJECT_MEMBER.REMOVED.isNull()));
    }
    projectMembers.addAll(create().selectFrom(PROJECT_MEMBER).where(allMembers).fetch());
    return !projectMembers.isEmpty();
  }

  public boolean isProjectMember(
      long projectId, Long usingAccount, boolean isAdmin, Set<Identity> identities) {
    if (identities == null) {
      return false;
    }
    if ((usingAccount != null && usingAccount.equals(projectId)) || isAdmin) {
      return true;
    }
    Set<ProjectMemberRecord> projectMembers = new HashSet<>();
    Condition allMembers = DSL.falseCondition();
    for (Identity id : identities) {
      allMembers =
          allMembers.or(
              PROJECT_MEMBER
                  .EXTERNAL_ID
                  .eq(id.getExternalId())
                  .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(id.getExternalIdType()))
                  .and(PROJECT_MEMBER.PROJECT_ID.eq(projectId))
                  .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE))
                  .and(PROJECT_MEMBER.REMOVED.isNull()));
    }
    projectMembers.addAll(create().selectFrom(PROJECT_MEMBER).where(allMembers).fetch());
    return !projectMembers.isEmpty();
  }

  @Override
  public List<? extends ProjectMember> setProjectMembers(
      final Account project, final Set<Member> members) {
    return lockManager.lock(
        new ProjectLock(project),
        new LockCallback<List<? extends ProjectMember>>() {
          public List<? extends ProjectMember> doWithLock() {
            List<? extends ProjectMember> previousMembers =
                getActiveProjectMembers(project.getId());
            Set<Member> otherPreviosMembers = new HashSet<>();
            for (ProjectMember member : previousMembers) {
              String projectId =
                  (String)
                      ApiContext.getContext()
                          .getIdFormatter()
                          .formatId(objectManager.getType(Account.class), member.getProjectId());
              otherPreviosMembers.add(new Member(member, projectId));
            }
            Set<Member> create = new HashSet<Member>(members);
            Set<Member> delete = new HashSet<Member>(otherPreviosMembers);
            for (Member member : members) {
              if (delete.remove(member)) {
                create.remove(member);
              }
            }
            Condition allMembers = DSL.falseCondition();
            for (Member member : delete) {
              allMembers =
                  allMembers.or(
                      PROJECT_MEMBER
                          .EXTERNAL_ID
                          .eq(member.getExternalId())
                          .and(PROJECT_MEMBER.EXTERNAL_ID_TYPE.eq(member.getExternalIdType()))
                          .and(PROJECT_MEMBER.PROJECT_ID.eq(project.getId()))
                          .and(PROJECT_MEMBER.STATE.eq(CommonStatesConstants.ACTIVE)));
            }
            List<? extends ProjectMember> toDelete =
                create().selectFrom(PROJECT_MEMBER).where(allMembers).fetch();
            for (ProjectMember member : toDelete) {
              objectProcessManager.executeStandardProcess(StandardProcess.DEACTIVATE, member, null);
              objectProcessManager.executeStandardProcess(StandardProcess.REMOVE, member, null);
            }
            List<ProjectMember> newMembers = new ArrayList<>();
            for (Member member : create) {
              newMembers.add(createProjectMember(project, member));
            }
            return getActiveProjectMembers(project.getId());
          }
        });
  }

  public ProjectMember createProjectMember(Account project, Member member) {
    Map<Object, Object> properties = new HashMap<>();
    properties.put(PROJECT_MEMBER.PROJECT_ID, project.getId());
    properties.put(PROJECT_MEMBER.ACCOUNT_ID, project.getId());
    properties.put(PROJECT_MEMBER.NAME, member.getName());
    properties.put(PROJECT_MEMBER.EXTERNAL_ID, member.getExternalId());
    properties.put(PROJECT_MEMBER.EXTERNAL_ID_TYPE, member.getExternalIdType());
    properties.put(PROJECT_MEMBER.ROLE, member.getRole());
    return resourceDao.create(
        ProjectMember.class, objectManager.convertToPropertiesFor(ProjectMember.class, properties));
  }

  @Override
  public void ensureAllProjectsHaveNonRancherIdMembers(Identity identity) {
    // This operation is expensive if there are alot of projects and members however this is
    // only called when auth is being turned on. In most cases this will only be called once.
    Member newMember = new Member(identity, ProjectConstants.OWNER);
    Set<Identity> identities = new HashSet<>();
    Account account = getAdminAccount();
    identities.add(new Identity(ProjectConstants.RANCHER_ID, String.valueOf(account.getId())));
    List<Account> allProjects = getAccessibleProjects(identities, true, null);
    for (Account project : allProjects) {
      List<? extends ProjectMember> members = getActiveProjectMembers(project.getId());
      boolean hasNonRancherMember = false;
      for (ProjectMember member : members) {
        if (!member.getExternalIdType().equalsIgnoreCase(ProjectConstants.RANCHER_ID)) {
          hasNonRancherMember = true;
        } else if (member.getExternalId().equals(String.valueOf(getAdminAccount().getId()))) {
          deactivateThenRemove(member);
        } else {
          hasNonRancherMember = true;
        }
      }
      if (!hasNonRancherMember) {
        createProjectMember(project, newMember);
      }
    }
  }

  private void deactivateThenRemove(ProjectMember member) {
    Object state = ObjectUtils.getPropertyIgnoreErrors(member, ObjectMetaDataManager.STATE_FIELD);

    if (CommonStatesConstants.ACTIVE.equals(state)) {
      objectProcessManager.executeStandardProcess(StandardProcess.DEACTIVATE, member, null);
      member = objectManager.reload(member);
    }

    if (CommonStatesConstants.PURGED.equals(state)) {
      return;
    }

    objectProcessManager.executeStandardProcess(StandardProcess.REMOVE, member, null);
  }

  @Override
  public boolean hasAccessToAnyProject(
      Set<Identity> identities, boolean isAdmin, Long usingAccount) {
    return !getAccessibleProjects(identities, isAdmin, usingAccount).isEmpty();
  }
}
public class RedisEventingService extends AbstractThreadPoolingEventService
    implements InitializationTask {

  private static final DynamicStringProperty REDIS_HOST = ArchaiusUtil.getString("redis.hosts");

  volatile List<RedisConnection> connections = new ArrayList<RedisConnection>();
  int index = 0;

  public void reconnect() {
    List<RedisConnection> newConnections = new ArrayList<RedisConnection>();

    for (String host : REDIS_HOST.get().trim().split("\\s*,\\s*")) {
      String[] parts = host.split(":");
      if (parts.length > 2) {
        throw new IllegalArgumentException(
            "Invalid redis host [" + host + "] should be in host:port format");
      }

      String hostName = parts[0];
      int port = Protocol.DEFAULT_PORT;

      if (parts.length > 1) {
        try {
          port = Integer.parseInt(parts[1]);
        } catch (NumberFormatException e) {
          throw new IllegalArgumentException(
              "Invalid redis host [" + host + "] should be in host:port format", e);
        }
      }

      newConnections.add(new RedisConnection(this, hostName, port));
    }

    List<RedisConnection> oldConnections = connections;
    connections = newConnections;

    for (RedisConnection conn : newConnections) {
      getExecutorService().submit(conn);
    }

    for (RedisConnection conn : oldConnections) {
      conn.stop();
    }
  }

  @Override
  public void start() {
    super.start();

    reconnect();

    REDIS_HOST.addCallback(
        new Runnable() {
          @Override
          public void run() {
            reconnect();
          }
        });
  }

  @Override
  public void stop() {
    super.stop();

    for (RedisConnection conn : connections) {
      conn.stop();
    }
  }

  protected void onMessage(String pattern, String channel, String message) {
    onEvent(pattern, channel, message);
  }

  @Override
  protected boolean doPublish(String name, Event event, String eventString) throws IOException {
    if (connections.size() == 0) {
      return false;
    }

    RedisConnection conn = null;
    try {
      conn = connections.get(index);
    } catch (IndexOutOfBoundsException e) {
      if (connections.size() > 0) {
        conn = connections.get(0);
      }
    }

    index = (index + 1) % connections.size();

    if (conn != null) {
      return conn.publish(name, eventString);
    }

    return false;
  }

  @Override
  protected void doSubscribe(String eventName, final SettableFuture<?> future) {
    List<SettableFuture<?>> futures = new ArrayList<SettableFuture<?>>();

    for (RedisConnection conn : connections) {
      SettableFuture<?> newFuture = SettableFuture.create();
      conn.subscribe(eventName, newFuture);
      futures.add(newFuture);
    }

    final ListenableFuture<?> combined = Futures.allAsList(futures);

    combined.addListener(
        new Runnable() {
          @Override
          public void run() {
            try {
              combined.get();
              future.set(null);
            } catch (InterruptedException e) {
              future.setException(e);
            } catch (ExecutionException e) {
              future.setException(e.getCause());
            }
          }
        },
        getExecutorService());
  }

  @Override
  protected void doUnsubscribe(String eventName) {
    for (RedisConnection conn : connections) {
      conn.unsubscribe(eventName);
    }
  }

  @Override
  protected void disconnect() {
    for (RedisConnection conn : connections) {
      conn.disconnect();
    }
  }
}
Exemple #3
0
public class SubSchemaFactory extends AbstractSchemaFactory implements SchemaFactory {

  private static DynamicBooleanProperty SERIALIZE = ArchaiusUtil.getBoolean("serialize.schemas");

  SchemaFactory schemaFactory;
  String id;
  Map<String, Schema> schemaMap;
  List<SchemaImpl> schemaList = new ArrayList<SchemaImpl>();
  List<SchemaPostProcessor> postProcessors = new ArrayList<SchemaPostProcessor>();
  boolean init = false;

  public synchronized void init() {
    if (init) {
      return;
    }

    schemaMap = new HashMap<String, Schema>();

    if (schemaFactory instanceof SubSchemaFactory) {
      ((SubSchemaFactory) schemaFactory).init();
    }

    List<SchemaImpl> result = new ArrayList<SchemaImpl>();

    for (Schema schema : schemaFactory.listSchemas()) {
      if (schema instanceof SchemaImpl) {
        /* Copy */
        SchemaImpl impl = new SchemaImpl((SchemaImpl) schema);

        for (SchemaPostProcessor post : postProcessors) {
          impl = post.postProcessRegister(impl, this);
          if (impl == null) {
            break;
          }
        }

        if (impl != null) {
          result.add(impl);
          schemaMap.put(impl.getId(), impl);
        }
      }
    }

    schemaList = result;

    for (SchemaImpl schema : schemaList) {
      for (SchemaPostProcessor post : postProcessors) {
        schema = post.postProcess(schema, this);
      }
    }

    for (SchemaImpl schema : schemaList) {
      prune(schema);
    }

    if (SERIALIZE.get()) {
      serializeSchema(listSchemas(), getId());
      synchronized (schemaFactory) {
        serializeSchema(schemaFactory.listSchemas(), schemaFactory.getId());
      }
    }

    init = true;
  }

  protected static void serializeSchema(List<Schema> schemaList, String id) {
    try (FileOutputStream fos = new FileOutputStream(new File(id + ".ser"))) {
      ObjectOutputStream oos = new ObjectOutputStream(fos);
      oos.writeObject(schemaList);
      oos.close();
    } catch (IOException e) {
      throw new IllegalStateException(e);
    }
  }

  protected void prune(SchemaImpl schema) {
    Map<String, Field> fields = schema.getResourceFields();
    Map<String, Filter> filters = schema.getCollectionFilters();

    for (String name : new HashSet<String>(fields.keySet())) {
      Field field = fields.get(name);

      List<FieldType> subTypeEnums = field.getSubTypeEnums();
      List<String> subTypes = field.getSubTypes();

      for (int i = 0; i < subTypeEnums.size(); i++) {
        if (subTypeEnums.get(i) == FieldType.TYPE
            && !schemaMap.containsKey(subTypes.get(i))
            && !"type".equals(subTypes.get(i))) {
          fields.remove(name);
          filters.remove(name);
          break;
        }
      }
    }

    Iterator<String> childrenIter = schema.getChildren().iterator();
    while (childrenIter.hasNext()) {
      if (!schemaMap.containsKey(childrenIter.next())) {
        childrenIter.remove();
      }
    }
  }

  @Override
  public String getId() {
    return id;
  }

  @SuppressWarnings({"unchecked", "rawtypes"})
  @Override
  public List<Schema> listSchemas() {
    return (List) schemaList;
  }

  @Override
  public Schema getSchema(Class<?> clz) {
    return getSchema(schemaFactory.getSchemaName(clz));
  }

  @Override
  public Schema getSchema(String type) {
    Schema parentSchema = schemaFactory.getSchema(type);
    return parentSchema == null ? null : schemaMap.get(parentSchema.getId());
  }

  @Override
  public Class<?> getSchemaClass(String type) {
    Schema schema = getSchema(type);
    return schema == null ? null : schemaFactory.getSchemaClass(type);
  }

  @Override
  public Schema registerSchema(Object obj) {
    throw new UnsupportedOperationException();
  }

  @Override
  public Schema parseSchema(String name) {
    throw new UnsupportedOperationException();
  }

  public SchemaFactory getSchemaFactory() {
    return schemaFactory;
  }

  public List<SchemaPostProcessor> getPostProcessors() {
    return postProcessors;
  }

  public void setPostProcessors(List<SchemaPostProcessor> postProcessors) {
    this.postProcessors = postProcessors;
  }

  public void setId(String id) {
    this.id = id;
  }

  public void setSchemaFactory(SchemaFactory schemaFactory) {
    this.schemaFactory = schemaFactory;
  }
}