public class ServiceFactory {
    private static RepositoryFactory REPOSITORY_FACTORY = new RepositoryFactoryImpl();
    private static PermissionManager PERMISSION_MANAGER = new PermissionManagerImpl(
            REPOSITORY_FACTORY, new JavascriptEvaluator());

    public static RepositoryFactory getDefaultFactory() {
        return REPOSITORY_FACTORY;
    }

    public static PermissionManager getPermissionManager() {
        return PERMISSION_MANAGER;
    }
} @Entity
@XmlRootElement
public class Domain extends PersistentObject implements Validatable,
        Identifiable<String> {
    public static final String DEFAULT_DOMAIN_NAME = Configuration
            .getInstance().getProperty("default.domain", "default");

    @PrimaryKey
    private String id;
    private String description;
    // @SecondaryKey(relate = Relationship.MANY_TO_MANY, relatedEntity =
    // Subject.class, onRelatedEntityDelete = DeleteAction.NULLIFY)
    Set<String> ownerSubjectNames = new HashSet<String>();

    // for JPA
    Domain() {
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public Domain(final String id, final String description) {
        setId(id);
        setDescription(description);
    }

    void setId(final String id) {
        if (GenericValidator.isBlankOrNull(id)) {
            throw new IllegalArgumentException("id is not specified");
        }
        this.id = id;
    }

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

    public Set<String> getOwnerSubjectNames() {
        return new HashSet<String>(ownerSubjectNames);
    }

    public void setOwnerSubjectNames(final Set<String> ownerSubjectNames) {
        firePropertyChange("ownerSubjectNames", this.ownerSubjectNames,
                ownerSubjectNames);

        this.ownerSubjectNames.clear();
        this.ownerSubjectNames.addAll(ownerSubjectNames);
    }

    public void addOwner(final String subjectName) {
        if (GenericValidator.isBlankOrNull(subjectName)) {
            throw new IllegalArgumentException("subjectName is not specified");
        }
        Set<String> old = getOwnerSubjectNames();
        this.ownerSubjectNames.add(subjectName);
        firePropertyChange("ownerSubjectNames", old, this.ownerSubjectNames);

    }

    public void addOwner(final Subject subject) {
        if (subject == null) {
            throw new IllegalArgumentException("subject is not specified");
        }
        addOwner(subject.getId());
    }

    public void removeOwner(final Subject subject) {
        if (subject == null) {
            throw new IllegalArgumentException("subject is not specified");
        }
        removeOwner(subject.getId());
    }

    public void removeOwner(final String subjectName) {
        if (GenericValidator.isBlankOrNull(subjectName)) {
            throw new IllegalArgumentException("subjectName is not specified");
        }
        Set<String> old = getOwnerSubjectNames();
        this.ownerSubjectNames.remove(subjectName);
        firePropertyChange("ownerSubjectNames", old, this.ownerSubjectNames);
    }

    /**
     * @see java.lang.Object#equals(Object)
     */
    @Override
    public boolean equals(Object object) {
        if (!(object instanceof Domain)) {
            return false;
        }
        Domain rhs = (Domain) object;
        return new EqualsBuilder().append(this.id, rhs.id).isEquals();
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(786529047, 1924536713).append(this.id)
                .toHashCode();
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this).append("name", this.id).append(
                "owners", this.ownerSubjectNames).toString();
    }

    @Override
    public void validate() throws ValidationException {
        final Map<String, String> errorsByField = new HashMap<String, String>();
        if (GenericValidator.isBlankOrNull(id)) {
            errorsByField.put("name", "domain id is not specified");
        }
        if (errorsByField.size() > 0) {
            throw new ValidationException(errorsByField);
        }
    }

} public class ValidationException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    private final Map<String, String> errorsByField = new HashMap<String, String>();

    public ValidationException(String message, Throwable cause) {
        super(message, cause);
    }

    public ValidationException(String message) {
        super(message);
    }

    public ValidationException(Throwable cause) {
        super(cause);
    }

    public ValidationException(String message, Throwable cause,
            Map<String, String> errorsByField) {
        super(message, cause);
        this.errorsByField.putAll(errorsByField);
    }

    public ValidationException(String message, Map<String, String> errorsByField) {
        super(message);
        this.errorsByField.putAll(errorsByField);

    }

    public ValidationException(Throwable cause,
            Map<String, String> errorsByField) {
        super(cause);
        this.errorsByField.putAll(errorsByField);
    }

    public ValidationException(Map<String, String> errorsByField) {
        this.errorsByField.putAll(errorsByField);
    }

    public void addError(final String field, final String message) {
        this.errorsByField.put(field, message);
    }

    public Map<String, String> getValidationErrors() {
        return this.errorsByField;
    }

    @Override
    public String toString() {
        return super.toString() + " " + errorsByField;
    }
} public interface RestClient {
	public static int OK = 200;
	public static int OK_MIN = 200;
	public static int OK_MAX = 299;
	public static int OK_CREATED = 201;
	public static int OK_ACCEPTED = 202;
	public static int REDIRECT_PERMANENTLY = 301;
	public static int REDIRECT_FOUND = 302;
	public static int CLIENT_ERROR_BAD_REQUEST = 400;
	public static int CLIENT_ERROR_UNAUTHORIZED = 401;
	public static int CLIENT_ERROR_FORBIDDEN = 403;
	public static int CLIENT_ERROR_NOT_FOUND = 404;
	public static int CLIENT_ERROR_TIMEOUT = 408;
	public static int CLIENT_ERROR_CONFLICT = 409;
	public static int CLIENT_ERROR_PRECONDITION = 412;
	public static int SERVER_INTERNAL_ERROR = 500;
	public static int SERVICE_UNAVAILABLE = 503;

	Tuple get(final String path) throws IOException;

	Tuple put(final String path, final String body) throws IOException;

	int delete(final String path) throws IOException;

	Tuple post(final String path, final String body) throws IOException;

} public class JMXRegistrar {
    private static final Logger LOGGER = Logger.getLogger(JMXRegistrar.class);
    private MBeanServer mbeanServer;
    private static final JMXRegistrar INSTANCE = new JMXRegistrar();
    private Map<String, ServiceJMXBeanImpl> mbeans = new ConcurrentHashMap<String, ServiceJMXBeanImpl>();

    public static JMXRegistrar getInstance() {
        return INSTANCE;
    }

    /**
     * 
     * @param serviceClass
     *            -- service class
     * @return instance of ServiceJMXBean
     */
    public ServiceJMXBeanImpl register(final Class<?> serviceClass) {
        return register(serviceClass.getPackage().getName() + ":type="
                + serviceClass.getSimpleName());
    }

    public Collection<ServiceJMXBeanImpl> getServiceJMXBeans() {
        return Collections.unmodifiableCollection(mbeans.values());
    }

    /**
     * 
     * @param serviceName
     *            -- name of service (that is also used as object name)
     * @return instance of ServiceJMXBean
     */
    public ServiceJMXBeanImpl register(final String serviceName) {
        if (GenericValidator.isBlankOrNull(serviceName)) {
            throw new IllegalArgumentException("serviceName not specified");
        }
        synchronized (serviceName.intern()) {
            ServiceJMXBeanImpl mbean = mbeans.get(serviceName);
            if (mbean == null) {
                mbean = new ServiceJMXBeanImpl(serviceName);
                mbeans.put(serviceName, mbean);
                try {
                    ObjectName name = new ObjectName(serviceName);
                    mbeanServer.registerMBean(mbean, name);

                    if (LOGGER.isInfoEnabled()) {
                        LOGGER.info("regstered MXBean " + serviceName);
                    }
                } catch (InstanceAlreadyExistsException e) {
                } catch (Exception e) {
                    throw new JMRuntimeException("Failed to register "
                            + serviceName + " due to " + e);
                }
            }
            return mbean;
        }
    }

    /**
     * @see java.lang.Object#equals(Object)
     */
    @Override
    public boolean equals(Object object) {
        if (!(object instanceof JMXRegistrar)) {
            return false;
        }
        JMXRegistrar rhs = (JMXRegistrar) object;
        return new EqualsBuilder().append(this.mbeanServer, rhs.mbeanServer)
                .isEquals();
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(786529047, 1924536713).append(mbeanServer)
                .toHashCode();
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("mbeanServer", this.mbeanServer).toString();
    }

    JMXRegistrar() {
        mbeanServer = ManagementFactory.getPlatformMBeanServer();
        if (mbeanServer == null) {
            List<MBeanServer> existingServers = MBeanServerFactory
                    .findMBeanServer(null);

            if (!existingServers.isEmpty()) {
                mbeanServer = existingServers.get(0);
            } else {
                mbeanServer = MBeanServerFactory.createMBeanServer();
            }
        }
        // registering log4j mbean
        final Log4jJMXBeanImpl mbean = new Log4jJMXBeanImpl();
        ObjectName name;
        try {
            name = new ObjectName(mbean.getObjectName());
            mbeanServer.registerMBean(mbean, name);
        } catch (Exception e) {
            LOGGER.error("Failed to register log4j-mbean", e);
        }

    }

} public class ServiceJMXBeanImpl extends NotificationBroadcasterSupport
        implements ServiceJMXBean {
    private Map<String, String> properties = new ConcurrentHashMap<String, String>();
    private final String serviceName;
    private AtomicLong totalErrors;
    private AtomicLong totalRequests;

    private AtomicLong sequenceNumber;
    private String state;

    public ServiceJMXBeanImpl(final String serviceName) {
        this.serviceName = serviceName;
        this.totalErrors = new AtomicLong();
        this.totalRequests = new AtomicLong();
        this.sequenceNumber = new AtomicLong();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     */
    @Override
    public double getAverageElapsedTimeInNanoSecs() {
        return Metric.getMetric(getServiceName())
                .getAverageDurationInNanoSecs();
    }

    /*
     * (non-Javadoc)
     */
    @Override
    public String getProperty(final String name) {
        return properties.get(name);
    }

    /*
     * (non-Javadoc)
     * 
     * java.lang.String)
     */
    @Override
    public void setProperty(final String name, final String value) {
        final String oldValue = properties.put(name, value);
        final Notification notification = new AttributeChangeNotification(this,
                sequenceNumber.incrementAndGet(), TimeUtils
                        .getCurrentTimeMillis(), name + " changed", name,
                "String", oldValue, value);
        sendNotification(notification);
    }

    /*
     * (non-Javadoc)
     */
    @Override
    public String getServiceName() {
        return serviceName;
    }

    /*
     * (non-Javadoc)
     */
    @Override
    public long getTotalDurationInNanoSecs() {
        return Metric.getMetric(getServiceName()).getTotalDurationInNanoSecs();
    }

    /*
     * (non-Javadoc)
     */
    @Override
    public long getTotalErrors() {
        return totalErrors.get();
    }

    public void incrementError() {
        final long oldErrors = totalErrors.getAndIncrement();
        final Notification notification = new AttributeChangeNotification(this,
                sequenceNumber.incrementAndGet(), TimeUtils
                        .getCurrentTimeMillis(), "Errors changed", "Errors",
                "long", oldErrors, oldErrors + 1);
        sendNotification(notification);
    }

    /*
     * (non-Javadoc)
     */
    @Override
    public long getTotalRequests() {
        return totalRequests.get();
    }

    public void incrementRequests() {
        final long oldRequests = totalRequests.getAndIncrement();
        final Notification notification = new AttributeChangeNotification(this,
                sequenceNumber.incrementAndGet(), TimeUtils
                        .getCurrentTimeMillis(), "Requests changed",
                "Requests", "long", oldRequests, oldRequests + 1);
        sendNotification(notification);
    }

    @Override
    public MBeanNotificationInfo[] getNotificationInfo() {
        String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE };
        String name = AttributeChangeNotification.class.getName();
        String description = "An attribute of this MBean has changed";
        MBeanNotificationInfo info = new MBeanNotificationInfo(types, name,
                description);
        return new MBeanNotificationInfo[] { info };
    }

    @Override
    public String getState() {
        return state;
    }

    /**
     * @param state
     *            the state to set
     */
    public void setState(String state) {
        this.state = state;
    }

    /**
     * @see java.lang.Object#equals(Object)
     */
    @Override
    public boolean equals(Object object) {
        if (!(object instanceof ServiceJMXBeanImpl)) {
            return false;
        }
        ServiceJMXBeanImpl rhs = (ServiceJMXBeanImpl) object;
        return new EqualsBuilder().append(this.serviceName, rhs.serviceName)
                .isEquals();
    }

    /**
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return new HashCodeBuilder(786529047, 1924536713).append(
                this.serviceName).toHashCode();
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("serviceName", this.serviceName).append("totalErrors",
                        this.totalErrors).append("totalRequests",
                        this.totalRequests).append("totalRequests",
                        this.totalRequests).append("state", this.state).append(
                        "properties", this.properties).toString();
    }
} public class NotFoundException extends PersistenceException {
    private static final long serialVersionUID = 1L;

    public NotFoundException(String message, Throwable cause) {
        super(message, cause);
    }

    public NotFoundException(String message) {
        super(message);
    }

    public NotFoundException(Throwable cause) {
        super(cause);
    }

} public class PersistenceException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    public PersistenceException(String message, Throwable cause) {
        super(message, cause);
    }

    public PersistenceException(String message) {
        super(message);
    }

    public PersistenceException(Throwable cause) {
        super(cause);
    }

} public interface RepositoryFactory {
    /**
     * 
     * @return instance of security repository
     */
    SecurityMappingRepository getSecurityMappingRepository(String domain);

    /**
     * 
     * @return instance of domain repository to manage domains
     */
    DomainRepository getDomainRepository();

    /**
     * 
     * @return high level domain for this application
     */
    Subject getSuperAdmin();

    /**
     * 
     * @return high level domain for this application
     */
    Domain getDefaultDomain();

    /**
     * 
     * @param domain
     * @return repository of roles for specific domain
     */
    RoleRepository getRoleRepository(String domain);

    /**
     * 
     * @param domain
     * @return repository of permissions for given domain
     */
    PermissionRepository getPermissionRepository(String domain);

    /**
     * 
     * @param domain
     * @return repository of security errors for given domain
     */
    SecurityErrorRepository getSecurityErrorRepository(String domain);

    /**
     * 
     * @param domain
     * @return repository of subjects for given domain
     */
    SubjectRepository getSubjectRepository(String domain);

    /**
     * 
     * @return
     */
    SubjectRepository getDefaultSubjectRepository();
} public interface DomainsService {
    Response get(String domain);

    Response index(String lastKey, int limit);

    Response put(Domain domain);

    Response delete(String domain);

    Response delete();
}
import java.util.Collection;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.validator.GenericValidator;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.plexobject.rbac.ServiceFactory;
import com.plexobject.rbac.domain.Domain;
import com.plexobject.rbac.domain.ValidationException;
import com.plexobject.rbac.http.RestClient;
import com.plexobject.rbac.jmx.JMXRegistrar;
import com.plexobject.rbac.jmx.impl.ServiceJMXBeanImpl;
import com.plexobject.rbac.repository.NotFoundException;
import com.plexobject.rbac.repository.PersistenceException;
import com.plexobject.rbac.repository.RepositoryFactory;
import com.plexobject.rbac.service.DomainsService;
import com.sun.jersey.spi.inject.Inject;
package com.plexobject.rbac.service.impl;





@Path("/security/domains")
@Component("domainsService")
@Scope("singleton")
public class DomainsServiceImpl implements DomainsService, InitializingBean {
    private static final Logger LOGGER = Logger
            .getLogger(AuthorizationServiceImpl.class);

    @Autowired
    @Inject
    RepositoryFactory repositoryFactory = ServiceFactory.getDefaultFactory();

    private final ServiceJMXBeanImpl mbean;

    public DomainsServiceImpl() {
        mbean = JMXRegistrar.getInstance().register(getClass());
    }

    @DELETE
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes( { MediaType.WILDCARD })
    @Path("/{domain}")
    @Override
    public Response delete(@PathParam("domain") String domainName) {
        if (GenericValidator.isBlankOrNull(domainName)) {
            return Response.status(RestClient.CLIENT_ERROR_BAD_REQUEST).type(