mirror of
https://gitee.com/jmix/cuba.git
synced 2024-12-04 20:28:00 +08:00
Refs #1774 Move REST API to portal
This commit is contained in:
parent
49c8521df5
commit
8461de1740
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.cuba.core.Locator;
|
||||
import com.haulmont.cuba.core.sys.AppContext;
|
||||
import com.haulmont.cuba.core.sys.SecurityContext;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
import com.haulmont.cuba.security.sys.UserSessionManager;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 25.04.2011
|
||||
* Time: 15:36:34
|
||||
*/
|
||||
public class Authentication {
|
||||
public static Authentication me(String sessionId) {
|
||||
UserSession userSession = getSession(sessionId);
|
||||
if (userSession == null) {
|
||||
return null;
|
||||
}
|
||||
AppContext.setSecurityContext(new SecurityContext(userSession));
|
||||
return new Authentication();
|
||||
}
|
||||
|
||||
public void forget() {
|
||||
AppContext.setSecurityContext(null);
|
||||
}
|
||||
|
||||
private static UserSession getSession(String sessionIdStr) {
|
||||
UUID sessionId;
|
||||
try {
|
||||
sessionId = UUID.fromString(sessionIdStr);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return ((UserSessionManager) Locator.lookup(UserSessionManager.NAME)).getSession(sessionId);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.cuba.core.global.EntityLoadInfo;
|
||||
import com.haulmont.cuba.core.global.UuidProvider;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 27.04.2011
|
||||
* Time: 0:55:10
|
||||
* $Id$
|
||||
*/
|
||||
public class CommitRequest {
|
||||
private Collection commitInstances;
|
||||
private Collection removeInstances;
|
||||
private boolean softDeletion = true;
|
||||
private HashSet<String> newInstanceIds = new HashSet<String>();
|
||||
private Map<String, InstanceRef> instanceRefs = new HashMap<String, InstanceRef>();
|
||||
private Set<String> commitIds = new HashSet<>();
|
||||
|
||||
public Collection getCommitInstances() {
|
||||
return commitInstances == null ? Collections.emptyList() : commitInstances;
|
||||
}
|
||||
|
||||
public Collection getRemoveInstances() {
|
||||
return removeInstances == null ? Collections.emptyList() : removeInstances;
|
||||
}
|
||||
|
||||
public boolean isSoftDeletion() {
|
||||
return softDeletion;
|
||||
}
|
||||
|
||||
public void setCommitInstances(Collection commitInstances) {
|
||||
this.commitInstances = commitInstances;
|
||||
}
|
||||
|
||||
public void setRemoveInstances(Collection removeInstances) {
|
||||
this.removeInstances = removeInstances;
|
||||
}
|
||||
|
||||
public void setSoftDeletion(boolean softDeletion) {
|
||||
this.softDeletion = softDeletion;
|
||||
}
|
||||
|
||||
public Collection getNewInstanceIds() {
|
||||
return Collections.unmodifiableSet(newInstanceIds);
|
||||
}
|
||||
|
||||
public Set<String> getCommitIds() {
|
||||
return commitIds;
|
||||
}
|
||||
|
||||
public void setCommitIds(Set<String> commitIds) {
|
||||
this.commitIds = commitIds;
|
||||
}
|
||||
|
||||
public InstanceRef parseInstanceRefAndRegister(String id) throws InstantiationException, IllegalAccessException {
|
||||
boolean isNew = false;
|
||||
if (id.startsWith("NEW-")) {
|
||||
id = id.substring("NEW-".length());
|
||||
isNew = true;
|
||||
}
|
||||
|
||||
InstanceRef existingRef = instanceRefs.get(id);
|
||||
if (existingRef != null) {
|
||||
return existingRef;
|
||||
}
|
||||
|
||||
EntityLoadInfo loadInfo = EntityLoadInfo.parse(id);
|
||||
if (loadInfo == null) {
|
||||
if (isNew) {
|
||||
UUID uuid = UuidProvider.createUuid();
|
||||
id = id + "-" + uuid;
|
||||
loadInfo = EntityLoadInfo.parse(id);
|
||||
if (loadInfo == null) {
|
||||
throw new RuntimeException("Cannot parse id: " + id);
|
||||
}
|
||||
} else
|
||||
throw new RuntimeException("Cannot parse id: " + id);
|
||||
}
|
||||
|
||||
if (isNew)
|
||||
newInstanceIds.add(id);
|
||||
|
||||
InstanceRef result = new InstanceRef(loadInfo);
|
||||
instanceRefs.put(id, result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--
|
||||
~ Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
~ Haulmont Technology proprietary and confidential.
|
||||
~ Use is subject to license terms.
|
||||
-->
|
||||
|
||||
<!-- ========================================================================= -->
|
||||
<!-- Schema for serialized persistence instance. -->
|
||||
<!-- ========================================================================= -->
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
attributeFormDefault="unqualified" elementFormDefault="qualified"
|
||||
version="1.0">
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Describes CommitRequest structure.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<!-- The root element of the document -->
|
||||
<xsd:element name="CommitRequest">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="commitInstances" minOccurs="0" maxOccurs="1">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="instance" minOccurs="0" maxOccurs="unbounded" type="instance-type"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="newInstanceIds" minOccurs="0" maxOccurs="1">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="newId" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="deleteInstances" minOccurs="0" maxOccurs="1">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="instance" minOccurs="0" maxOccurs="unbounded" type="instance-type"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="version" type="xsd:string"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<!-- The root element for a single instance. Children of this element are persistent attribute -->
|
||||
<!-- Persistent Attributes occur in order. The order is determined by the attribute category. -->
|
||||
<!-- Attribute category is determined by the enumerated PersistentAttributeType defined in -->
|
||||
<!-- javax.persistence.metamodel and then further refined by id, version, lob and enum. -->
|
||||
<xsd:complexType name="instance-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="id" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="version" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="basic" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="enum" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="embedded" type="instance-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="lob" type="lob-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="one-to-one" type="singular-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="many-to-one" type="singular-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="element-collection" type="collection-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="one-to-many" type="collection-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="many-to-many" type="map-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- A reference to another instance within the same(?) document -->
|
||||
<xsd:complexType name="ref-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="id" type="xsd:IDREF"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- A null reference -->
|
||||
<xsd:complexType name="ref-null">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Basic Attribute has a name and its runtime type -->
|
||||
<!-- non-null value appears as text content. -->
|
||||
<!-- null value appears as attribute with empty text . -->
|
||||
<xsd:complexType name="basic-attr-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="null" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Large Binary Objects (LOB) represented as hex array -->
|
||||
<xsd:complexType name="lob-attr-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:hexBinary">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="null" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Singular attribute is a reference to another instance or a null reference. -->
|
||||
<xsd:complexType name="singular-attr-type">
|
||||
<xsd:choice>
|
||||
<xsd:element name="null" type="ref-null"/>
|
||||
<xsd:element name="ref" type="ref-type"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Collection attributes list their members with their runtime type -->
|
||||
<!-- Members can be basic or other managed instance -->
|
||||
<xsd:complexType name="collection-attr-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="member" type="member-type" minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="member-type" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Map attributes list their entries with runtime type of key and value -->
|
||||
<!-- Both key and value can be independently basic or other managed instance -->
|
||||
<xsd:complexType name="map-attr-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="entry" type="entry-type"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="key-type" type="xsd:string" use="required"/>
|
||||
<xsd:attribute name="value-type" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Value of a member of basic type. -->
|
||||
<xsd:complexType name="basic-value-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="null" type="xsd:boolean"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Value of a member of a collection/map -->
|
||||
<xsd:complexType name="member-type">
|
||||
<xsd:choice>
|
||||
<xsd:element name="basic" type="basic-value-type"/>
|
||||
<xsd:element name="null" type="ref-null"/>
|
||||
<xsd:element name="ref" type="ref-type"/>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Denotes entry of a map element -->
|
||||
<xsd:complexType name="entry-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="key" type="member-type" minOccurs="1" maxOccurs="1"/>
|
||||
<xsd:element name="value" type="member-type" minOccurs="1" maxOccurs="1"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 26.04.2011
|
||||
* Time: 2:00:09
|
||||
*/
|
||||
public class ConversionFactory {
|
||||
private Map<String, Convertor> convertors = new HashMap<String, Convertor>();
|
||||
|
||||
public ConversionFactory() {
|
||||
convertors.put("xml", new XMLConvertor());
|
||||
convertors.put("json", new JSONConvertor());
|
||||
}
|
||||
|
||||
public Convertor getConvertor(MimeType requestedForm) {
|
||||
if (requestedForm == null) {
|
||||
return convertors.values().iterator().next();
|
||||
}
|
||||
|
||||
for (Convertor convertor : convertors.values()) {
|
||||
if (requestedForm.match(convertor.getMimeType())) {
|
||||
return convertor;
|
||||
}
|
||||
}
|
||||
return convertors.values().iterator().next();
|
||||
}
|
||||
|
||||
public Convertor getConvertor(String type) {
|
||||
Convertor convertor = convertors.get(type);
|
||||
if (convertor == null)
|
||||
convertors.values().iterator().next();
|
||||
return convertor;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface Convertor {
|
||||
public MimeType getMimeType();
|
||||
|
||||
Object process(Entity entity, MetaClass metaclass, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;
|
||||
|
||||
Object process(List<Entity> entities, MetaClass metaClass, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;
|
||||
|
||||
Object process(Map<Entity, Entity> entityMap, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException;
|
||||
|
||||
CommitRequest parseCommitRequest(String content);
|
||||
|
||||
void write(HttpServletResponse response, Object o) throws IOException;
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 20.04.2011
|
||||
* Time: 18:13:08
|
||||
*/
|
||||
public class ConvertorHelper {
|
||||
public static final Comparator<MetaProperty> PROPERTY_COMPARATOR = new Comparator<MetaProperty>() {
|
||||
public int compare(MetaProperty p1, MetaProperty p2) {
|
||||
return p1.getName().compareTo(p2.getName());
|
||||
}
|
||||
};
|
||||
|
||||
public static List<MetaProperty> getOrderedProperties(MetaClass metaClass) {
|
||||
List<MetaProperty> result = new ArrayList<MetaProperty>(metaClass.getProperties());
|
||||
Collections.sort(result, PROPERTY_COMPARATOR);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.chile.core.datatypes.Datatype;
|
||||
import com.haulmont.chile.core.datatypes.Datatypes;
|
||||
import com.haulmont.chile.core.datatypes.impl.*;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.app.DataService;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.core.sys.restapi.template.MetaClassRepresentation;
|
||||
import com.haulmont.cuba.security.entity.EntityOp;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapper;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 19.04.2011
|
||||
* Time: 22:03:25
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
@Controller
|
||||
public class DataServiceController {
|
||||
private static Log log = LogFactory.getLog(DataServiceController.class);
|
||||
private DataService svc;
|
||||
//todo wire
|
||||
private ConversionFactory conversionFactory = new ConversionFactory();
|
||||
private Collection availableBasicTypes;
|
||||
|
||||
@Inject
|
||||
private MetadataTools metadataTools;
|
||||
|
||||
@Inject
|
||||
public DataServiceController(DataService svc) {
|
||||
this.svc = svc;
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/find.{type}", method = RequestMethod.GET)
|
||||
public void find(@PathVariable String type,
|
||||
@RequestParam(value = "e") String entityRef,
|
||||
@RequestParam(value = "s") String sessionId,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
Authentication authentication = Authentication.me(sessionId);
|
||||
if (authentication == null) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
EntityLoadInfo loadInfo = EntityLoadInfo.parse(entityRef);
|
||||
if (loadInfo == null) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
MetaClass metaClass = loadInfo.getMetaClass();
|
||||
if (!readPermitted(metaClass)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
UUID idObject = loadInfo.getId();
|
||||
|
||||
try {
|
||||
LoadContext loadCtx = new LoadContext(metaClass);
|
||||
loadCtx.setId(idObject);
|
||||
loadCtx.setUseSecurityConstraints(true);
|
||||
if (loadInfo.getViewName() != null)
|
||||
loadCtx.setView(loadInfo.getViewName());
|
||||
|
||||
Entity entity = svc.load(loadCtx);
|
||||
if (entity == null) {
|
||||
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||
} else {
|
||||
Convertor convertor = conversionFactory.getConvertor(type);
|
||||
Object result = convertor.process(entity, metaClass, request.getRequestURI());
|
||||
convertor.write(response, result);
|
||||
}
|
||||
} finally {
|
||||
authentication.forget();
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/query.{type}", method = RequestMethod.GET)
|
||||
public void query(@PathVariable String type,
|
||||
@RequestParam(value = "e") String entityName,
|
||||
@RequestParam(value = "q") String queryStr,
|
||||
@RequestParam(value = "view", required = false) String view,
|
||||
@RequestParam(value = "first", required = false) Integer firstResult,
|
||||
@RequestParam(value = "max", required = false) Integer maxResults,
|
||||
@RequestParam(value = "s") String sessionId,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
|
||||
Authentication authentication = Authentication.me(sessionId);
|
||||
if (authentication == null) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
MetaClass metaClass = getMetaClass(entityName);
|
||||
if (metaClass == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown entity name " + entityName);
|
||||
}
|
||||
|
||||
EntityOp queryEntityOp = getQueryEntityOp(queryStr);
|
||||
if (!entityOpPermitted(metaClass, queryEntityOp)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, String[]> queryParams = new HashMap<String, String[]>(request.getParameterMap());
|
||||
queryParams.remove("e");
|
||||
queryParams.remove("q");
|
||||
queryParams.remove("view");
|
||||
queryParams.remove("first");
|
||||
queryParams.remove("s");
|
||||
queryParams.remove("max");
|
||||
try {
|
||||
LoadContext loadCtx = new LoadContext(metaClass);
|
||||
loadCtx.setUseSecurityConstraints(true);
|
||||
LoadContext.Query query = new LoadContext.Query(queryStr);
|
||||
loadCtx.setQuery(query);
|
||||
if (firstResult != null)
|
||||
query.setFirstResult(firstResult);
|
||||
if (maxResults != null)
|
||||
query.setMaxResults(maxResults);
|
||||
|
||||
for (Map.Entry<String, String[]> entry : queryParams.entrySet()) {
|
||||
String paramKey = entry.getKey();
|
||||
if (paramKey.endsWith("_type"))
|
||||
continue;
|
||||
|
||||
if (entry.getValue().length != 1) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
String paramValue = entry.getValue()[0];
|
||||
Object parsedParam = parseQueryParameter(paramKey, paramValue, queryParams);
|
||||
query.addParameter(paramKey, parsedParam);
|
||||
}
|
||||
|
||||
loadCtx.setView(view == null ? View.LOCAL : view);
|
||||
List<Entity> entities = svc.loadList(loadCtx);
|
||||
Convertor convertor = conversionFactory.getConvertor(type);
|
||||
Object result = convertor.process(entities, metaClass, request.getRequestURI());
|
||||
convertor.write(response, result);
|
||||
} finally {
|
||||
authentication.forget();
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/commit", method = RequestMethod.POST)
|
||||
public void commit(@RequestParam(value = "s") String sessionId,
|
||||
@RequestHeader(value = "Content-Type") MimeType contentType,
|
||||
@RequestBody String requestContent,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) throws
|
||||
IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
|
||||
Authentication authentication = Authentication.me(sessionId);
|
||||
if (authentication == null) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
|
||||
Convertor convertor = conversionFactory.getConvertor(contentType);
|
||||
try {
|
||||
CommitRequest commitRequest = convertor.parseCommitRequest(requestContent);
|
||||
Collection commitInstances = commitRequest.getCommitInstances();
|
||||
Collection newInstanceIds = commitRequest.getNewInstanceIds();
|
||||
//send error if the user don't have permissions to commit at least one of the entities
|
||||
if (!commitPermitted(commitInstances, newInstanceIds)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
Collection removeInstances = commitRequest.getRemoveInstances();
|
||||
//send error if the user don't have permissions to remove at least one of the entities
|
||||
if (!removePermitted(removeInstances)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
NotDetachedCommitContext commitContext = new NotDetachedCommitContext();
|
||||
commitContext.setCommitInstances(commitInstances);
|
||||
commitContext.setRemoveInstances(removeInstances);
|
||||
commitContext.setSoftDeletion(commitRequest.isSoftDeletion());
|
||||
commitContext.setNewInstanceIds(newInstanceIds);
|
||||
Map<Entity, Entity> result = svc.commitNotDetached(commitContext);
|
||||
|
||||
Object converted = convertor.process(result, request.getRequestURI());
|
||||
convertor.write(response, converted);
|
||||
} finally {
|
||||
authentication.forget();
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/deployViews", method = RequestMethod.POST)
|
||||
public void deployViews(@RequestParam(value = "s") String sessionId,
|
||||
@RequestBody String requestContent,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response) throws
|
||||
IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
|
||||
Authentication authentication = Authentication.me(sessionId);
|
||||
if (authentication == null) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
ViewRepository viewRepository = MetadataProvider.getViewRepository();
|
||||
try {
|
||||
viewRepository.deployViews(new StringReader(requestContent));
|
||||
} finally {
|
||||
authentication.forget();
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/printDomain", method = RequestMethod.GET)
|
||||
public void printDomain(@RequestParam(value = "s") String sessionId,
|
||||
HttpServletResponse response) throws
|
||||
IOException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, TemplateException {
|
||||
|
||||
Authentication authentication = Authentication.me(sessionId);
|
||||
if (authentication == null) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setLocale(UserSessionProvider.getLocale());
|
||||
PrintWriter writer = response.getWriter();
|
||||
|
||||
try {
|
||||
ViewRepository viewRepository = MetadataProvider.getViewRepository();
|
||||
List<View> views = viewRepository.getAll();
|
||||
Map<MetaClass, List<View>> meta2views = new HashMap<MetaClass, List<View>>();
|
||||
for (View view : views) {
|
||||
MetaClass metaClass = MetadataProvider.getSession().getClass(view.getEntityClass());
|
||||
if (!readPermitted(metaClass))
|
||||
continue;
|
||||
|
||||
List<View> viewList = meta2views.get(metaClass);
|
||||
if (viewList == null) {
|
||||
viewList = new ArrayList<View>();
|
||||
meta2views.put(metaClass, viewList);
|
||||
}
|
||||
viewList.add(view);
|
||||
}
|
||||
|
||||
List<MetaClassRepresentation> classes = new ArrayList<MetaClassRepresentation>();
|
||||
|
||||
Set<MetaClass> metas = new HashSet<MetaClass>(metadataTools.getAllPersistentMetaClasses());
|
||||
metas.addAll(metadataTools.getAllEmbeddableMetaClasses());
|
||||
for (MetaClass meta : metas) {
|
||||
if (!readPermitted(meta))
|
||||
continue;
|
||||
MetaClassRepresentation rep = new MetaClassRepresentation(meta, meta2views.get(meta));
|
||||
classes.add(rep);
|
||||
}
|
||||
Collections.sort(classes, new Comparator<MetaClassRepresentation>() {
|
||||
public int compare(MetaClassRepresentation o1, MetaClassRepresentation o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, Object> values = new HashMap<String, Object>();
|
||||
values.put("knownEntities", classes);
|
||||
|
||||
String[] availableTypes = getAvailableBasicTypes();
|
||||
values.put("availableTypes", availableTypes);
|
||||
Configuration cfg = new Configuration();
|
||||
cfg.setDefaultEncoding("UTF-8");
|
||||
cfg.setOutputEncoding("UTF-8");
|
||||
cfg.setClassForTemplateLoading(DataServiceController.class, "template");
|
||||
cfg.setObjectWrapper(new DefaultObjectWrapper());
|
||||
Template template = cfg.getTemplate("domain.ftl");
|
||||
template.process(values, writer);
|
||||
} finally {
|
||||
authentication.forget();
|
||||
}
|
||||
}
|
||||
|
||||
private Object parseQueryParameter(String paramKey, String paramValue, Map<String, String[]> queryParams) {
|
||||
String[] typeName = queryParams.get(paramKey + "_type");
|
||||
//if the type is specified
|
||||
if (typeName != null && typeName.length == 1) {
|
||||
return parseByTypename(paramValue, typeName[0]);
|
||||
}
|
||||
//if the type is not specified
|
||||
else if (typeName == null) {
|
||||
return tryParse(paramValue);
|
||||
}
|
||||
//if several types have been declared
|
||||
else {
|
||||
throw new IllegalStateException("Too many parameters in request");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse a string value into some of the available Datatypes
|
||||
* when no Datatype was specified.
|
||||
*
|
||||
* @param value value to parse
|
||||
* @return parsed value
|
||||
*/
|
||||
private Object tryParse(String value) {
|
||||
try {
|
||||
return parseByDatatypeName(value, UUIDDatatype.NAME);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
try {
|
||||
return parseByDatatypeName(value, DateTimeDatatype.NAME);
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
return parseByDatatypeName(value, TimeDatatype.NAME);
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
return parseByDatatypeName(value, DateDatatype.NAME);
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
return parseByDatatypeName(value, BigDecimalDatatype.NAME);
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
return parseByDatatypeName(value, DoubleDatatype.NAME);
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
try {
|
||||
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
|
||||
return parseByDatatypeName(value, BooleanDatatype.NAME);
|
||||
}
|
||||
} catch (ParseException ignored) {
|
||||
}
|
||||
//return string value if couldn't parse into specific type
|
||||
return value;
|
||||
}
|
||||
|
||||
private Object parseByDatatypeName(String value, String name) throws ParseException {
|
||||
Datatype datatype = Datatypes.get(name);
|
||||
return datatype.parse(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses string value into specific type
|
||||
* @param value value to parse
|
||||
* @param typeName Datatype name
|
||||
* @return parsed object
|
||||
*/
|
||||
private Object parseByTypename(String value, String typeName) {
|
||||
Datatype datatype = Datatypes.get(typeName);
|
||||
try {
|
||||
return datatype.parse(value);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Cannot parse specified parameter");
|
||||
}
|
||||
}
|
||||
|
||||
private MetaClass getMetaClass(String entityName) {
|
||||
Collection<MetaClass> classes = metadataTools.getAllPersistentMetaClasses();
|
||||
for (MetaClass metaClass : classes) {
|
||||
if (entityName.equals(metaClass.getName()))
|
||||
return metaClass;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user have permissions to commit (create or update)
|
||||
* all of the entities.
|
||||
*
|
||||
* @param commitInstances entities to commit
|
||||
* @param newInstanceIds ids of the new entities
|
||||
* @return true - if the user can commit all of the requested entities, false -
|
||||
* if he don't have permissions to commit at least one of the entities.
|
||||
*/
|
||||
private boolean commitPermitted(Collection commitInstances, Collection newInstanceIds) {
|
||||
for (Object commitInstance : commitInstances) {
|
||||
Entity entity = (Entity) commitInstance;
|
||||
String fullId = entity.getMetaClass().getName() + "-" + entity.getId();
|
||||
if (newInstanceIds.contains(fullId)) {
|
||||
if (!createPermitted(entity.getMetaClass()))
|
||||
return false;
|
||||
} else if (!updatePermitted(entity.getMetaClass())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user have permissions to remove all of the requested entities.
|
||||
*
|
||||
* @param removeInstances entities to remove
|
||||
* @return true - if the user can remove all of the requested entities, false -
|
||||
* if he don't have permissions to remove at least one of the entities.
|
||||
*/
|
||||
private boolean removePermitted(Collection removeInstances) {
|
||||
for (Object removeInstance : removeInstances) {
|
||||
Entity next = (Entity) removeInstance;
|
||||
if (!removePermitted(next.getMetaClass()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean readPermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.READ);
|
||||
}
|
||||
|
||||
private boolean createPermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.CREATE);
|
||||
}
|
||||
|
||||
private boolean updatePermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.UPDATE);
|
||||
}
|
||||
|
||||
private boolean removePermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.DELETE);
|
||||
}
|
||||
|
||||
private boolean entityOpPermitted(MetaClass metaClass, EntityOp entityOp) {
|
||||
UserSession session = UserSessionProvider.getUserSession();
|
||||
return session.isEntityOpPermitted(metaClass, entityOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns EntityOp query requests to
|
||||
*
|
||||
* @param query JPQL query
|
||||
* @return EntityOp.READ or EntityOp.UPDATE or EntityOp.DELETE or null
|
||||
*/
|
||||
private static EntityOp getQueryEntityOp(String query) {
|
||||
if (query == null)
|
||||
return null;
|
||||
|
||||
query = query.trim().toLowerCase();
|
||||
if (query.isEmpty())
|
||||
return null;
|
||||
|
||||
int firstSpaceIndex = query.indexOf(' ');
|
||||
int endIndex = firstSpaceIndex != -1 ? firstSpaceIndex : query.length();
|
||||
String op = query.substring(0, endIndex);
|
||||
if ("select".equals(op))
|
||||
return EntityOp.READ;
|
||||
else if ("update".equals(op))
|
||||
return EntityOp.UPDATE;
|
||||
else if ("delete".equals(op))
|
||||
return EntityOp.DELETE;
|
||||
return null;
|
||||
}
|
||||
|
||||
public String[] getAvailableBasicTypes() {
|
||||
Set<String> allAvailableTypes = Datatypes.getNames();
|
||||
TreeSet<String> availableTypes = new TreeSet<String>();
|
||||
|
||||
//byteArray is not supported as a GET parameter
|
||||
for (String type : allAvailableTypes)
|
||||
if (!"byteArray".equals(type))
|
||||
availableTypes.add(type);
|
||||
|
||||
return availableTypes.toArray(new String[availableTypes.size()]);
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.cuba.core.entity.BaseUuidEntity;
|
||||
import com.haulmont.cuba.core.global.EntityLoadInfo;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 14.05.2011
|
||||
* Time: 1:18:58
|
||||
*/
|
||||
public class InstanceRef {
|
||||
private EntityLoadInfo loadInfo;
|
||||
private BaseUuidEntity instance;
|
||||
|
||||
public InstanceRef(EntityLoadInfo loadInfo) throws InstantiationException, IllegalAccessException {
|
||||
if (loadInfo == null)
|
||||
throw new NullPointerException("No load info passed");
|
||||
|
||||
this.loadInfo = loadInfo;
|
||||
MetaClass childMetaClass = this.loadInfo.getMetaClass();
|
||||
instance = childMetaClass.createInstance();
|
||||
instance.setId(this.loadInfo.getId());
|
||||
}
|
||||
|
||||
public UUID getId() {
|
||||
return loadInfo.getId();
|
||||
}
|
||||
|
||||
public MetaClass getMetaClass() {
|
||||
return loadInfo.getMetaClass();
|
||||
}
|
||||
|
||||
public BaseUuidEntity getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
InstanceRef that = (InstanceRef) o;
|
||||
|
||||
if (!loadInfo.getId().equals(that.loadInfo.getId())) return false;
|
||||
if (!loadInfo.getMetaClass().equals(that.loadInfo.getMetaClass())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result1 = loadInfo.getId().hashCode();
|
||||
result1 = 31 * result1 + loadInfo.getMetaClass().hashCode();
|
||||
return result1;
|
||||
}
|
||||
}
|
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.chile.core.datatypes.impl.StringDatatype;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.cuba.core.entity.BaseUuidEntity;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.entity.EntityAttrAccess;
|
||||
import com.haulmont.cuba.security.entity.EntityOp;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import javax.activation.MimeTypeParseException;
|
||||
import javax.persistence.Id;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
public class JSONConvertor implements Convertor {
|
||||
public static final String MIME_STR = "application/json;charset=UTF-8";
|
||||
public static final MimeType MIME_TYPE_JSON;
|
||||
|
||||
static {
|
||||
try {
|
||||
MIME_TYPE_JSON = new MimeType(MIME_STR);
|
||||
} catch (MimeTypeParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public MimeType getMimeType() {
|
||||
return MIME_TYPE_JSON;
|
||||
}
|
||||
|
||||
public MyJSONObject process(Entity entity, MetaClass metaclass, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
return encodeInstance(entity, new HashSet<Entity>(), metaclass);
|
||||
}
|
||||
|
||||
public MyJSONObject.Array process(List<Entity> entities, MetaClass metaClass, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
MyJSONObject.Array result = new MyJSONObject.Array();
|
||||
for (Entity entity : entities) {
|
||||
MyJSON item = encodeInstance(entity, new HashSet<Entity>(), metaClass);
|
||||
result.add(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public MyJSONObject.Array process(Map<Entity, Entity> entityMap, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
MyJSONObject.Array result = new MyJSONObject.Array();
|
||||
for (Map.Entry<Entity, Entity> entry : entityMap.entrySet()) {
|
||||
Entity key = entry.getKey();
|
||||
Entity value = entry.getValue();
|
||||
MyJSONObject keyJson = encodeInstance(key, new HashSet<Entity>(), getMetaClass(key));
|
||||
MyJSONObject valueJson = encodeInstance(value, new HashSet<Entity>(), getMetaClass(value));
|
||||
|
||||
MyJSONObject.Array mapping = new MyJSONObject.Array();
|
||||
mapping.add(keyJson);
|
||||
mapping.add(valueJson);
|
||||
result.add(mapping);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private MetaClass getMetaClass(Entity entity) {
|
||||
return MetadataProvider.getSession().getClass(entity.getClass());
|
||||
}
|
||||
|
||||
public void write(HttpServletResponse response, Object o) {
|
||||
response.setContentType(MIME_STR);
|
||||
try {
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write(o.toString());
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public CommitRequest parseCommitRequest(String content) {
|
||||
try {
|
||||
JSONObject jsonContent = new JSONObject(content);
|
||||
CommitRequest result = new CommitRequest();
|
||||
|
||||
if (jsonContent.has("commitInstances")) {
|
||||
JSONArray entitiesNodeList = jsonContent.getJSONArray("commitInstances");
|
||||
|
||||
Set<String> commitIds = new HashSet<>(entitiesNodeList.length());
|
||||
for (int i = 0; i < entitiesNodeList.length(); i++) {
|
||||
String id = entitiesNodeList.getJSONObject(i).getString("id");
|
||||
if (id.startsWith("NEW-"))
|
||||
id = id.substring(id.indexOf('-') + 1);
|
||||
commitIds.add(id);
|
||||
}
|
||||
|
||||
result.setCommitIds(commitIds);
|
||||
result.setCommitInstances(parseIntoList(result, entitiesNodeList));
|
||||
}
|
||||
|
||||
if (jsonContent.has("removeInstances")) {
|
||||
JSONArray entitiesNodeList = jsonContent.getJSONArray("removeInstances");
|
||||
result.setRemoveInstances(parseIntoList(result, entitiesNodeList));
|
||||
}
|
||||
|
||||
if (jsonContent.has("softDeletion")) {
|
||||
result.setSoftDeletion(jsonContent.getBoolean("softDeletion"));
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<BaseUuidEntity> parseIntoList(CommitRequest commitRequest, JSONArray nodeList)
|
||||
throws JSONException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, IntrospectionException, ParseException {
|
||||
List<BaseUuidEntity> result = new ArrayList<BaseUuidEntity>(nodeList.length());
|
||||
|
||||
for (int j = 0; j < nodeList.length(); j++) {
|
||||
JSONObject jsonObject = nodeList.getJSONObject(j);
|
||||
InstanceRef ref = commitRequest.parseInstanceRefAndRegister(jsonObject.getString("id"));
|
||||
MetaClass metaClass = ref.getMetaClass();
|
||||
BaseUuidEntity instance = ref.getInstance();
|
||||
asJavaTree(commitRequest, instance, metaClass, jsonObject);
|
||||
result.add(instance);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void asJavaTree(CommitRequest commitRequest, Object bean, MetaClass metaClass, JSONObject json)
|
||||
throws JSONException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException, IntrospectionException, ParseException {
|
||||
Iterator iter = json.keys();
|
||||
while (iter.hasNext()) {
|
||||
String key = (String) iter.next();
|
||||
|
||||
//version is readonly property
|
||||
if ("version".equals(key))
|
||||
continue;
|
||||
|
||||
MetaProperty property = metaClass.getProperty(key);
|
||||
|
||||
if (!attrModifyPermitted(metaClass, property.getName()))
|
||||
continue;
|
||||
|
||||
if (json.get(key) == null) {
|
||||
setField(bean, key, new Object[]{null});
|
||||
continue;
|
||||
}
|
||||
|
||||
if ("id".equals(key)) {
|
||||
// id was parsed already
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (property.getType()) {
|
||||
case DATATYPE:
|
||||
String value = json.getString(key);
|
||||
String typeName = property.getRange().<Object>asDatatype().getName();
|
||||
if (!StringDatatype.NAME.equals(typeName) && "null".equals(value))
|
||||
value = null;
|
||||
setField(bean, key, property.getRange().<Object>asDatatype().parse(value));
|
||||
break;
|
||||
case ENUM:
|
||||
setField(bean, key, property.getRange().asEnumeration().parse(json.getString(key)));
|
||||
break;
|
||||
case COMPOSITION:
|
||||
case ASSOCIATION:
|
||||
if ("null".equals(json.getString(key))) {
|
||||
setField(bean, key, null);
|
||||
break;
|
||||
}
|
||||
MetaClass propertyMetaClass = propertyMetaClass(property);
|
||||
//checks if the user permitted to read and update a property
|
||||
if (!updatePermitted(propertyMetaClass) && !readPermitted(propertyMetaClass))
|
||||
break;
|
||||
|
||||
if (!property.getRange().getCardinality().isMany()) {
|
||||
JSONObject jsonChild = json.getJSONObject(key);
|
||||
Object child;
|
||||
MetaClass childMetaClass;
|
||||
|
||||
if (jsonChild.has("id")) {
|
||||
String id = jsonChild.getString("id");
|
||||
|
||||
//reference to an entity that also a commit instance
|
||||
//will be registered later
|
||||
if (commitRequest.getCommitIds().contains(id)) {
|
||||
EntityLoadInfo loadInfo = EntityLoadInfo.parse(id);
|
||||
BaseUuidEntity ref = loadInfo.getMetaClass().createInstance();
|
||||
ref.setValue("id", loadInfo.getId());
|
||||
setField(bean, key, ref);
|
||||
break;
|
||||
}
|
||||
|
||||
InstanceRef ref = commitRequest.parseInstanceRefAndRegister(id);
|
||||
childMetaClass = ref.getMetaClass();
|
||||
child = ref.getInstance();
|
||||
} else {
|
||||
childMetaClass = property.getRange().asClass();
|
||||
child = childMetaClass.createInstance();
|
||||
}
|
||||
asJavaTree(commitRequest, child, childMetaClass, jsonChild);
|
||||
setField(bean, key, child);
|
||||
} else {
|
||||
JSONArray jsonArray = json.getJSONArray(key);
|
||||
Collection<Object> coll = property.getRange().isOrdered()
|
||||
? new ArrayList<Object>()
|
||||
: new HashSet<Object>();
|
||||
setField(bean, key, coll);
|
||||
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
Object arrayValue = jsonArray.get(i);
|
||||
if (arrayValue == null)
|
||||
coll.add(null);
|
||||
else {
|
||||
//assuming no simple type here
|
||||
JSONObject jsonChild = (JSONObject) arrayValue;
|
||||
InstanceRef ref = commitRequest.parseInstanceRefAndRegister(jsonChild.getString("id"));
|
||||
Object child = ref.getInstance();
|
||||
coll.add(child);
|
||||
asJavaTree(commitRequest, child, ref.getMetaClass(), jsonChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unknown property type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setField(Object bean, String field, Object value)
|
||||
throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
new PropertyDescriptor(field, bean.getClass()).getWriteMethod().invoke(bean, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the closure of a persistent instance into JSON.
|
||||
*
|
||||
* @param entity the managed instance to be encoded. Can be null.
|
||||
* @param visited the persistent instances that had been encoded already. Must not be null or immutable.
|
||||
* @return the new element. The element has been appended as a child to the given parent in this method.
|
||||
*/
|
||||
private MyJSONObject encodeInstance(final Entity entity, final Set<Entity> visited, MetaClass metaClass)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
if (visited == null) {
|
||||
throw new IllegalArgumentException("null closure for encoder");
|
||||
}
|
||||
if (entity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean ref = !visited.add(entity);
|
||||
|
||||
MyJSONObject root = new MyJSONObject(idof(entity), ref);
|
||||
|
||||
if (ref) {
|
||||
return root;
|
||||
}
|
||||
|
||||
MetadataTools metadataTools = AppBeans.get(MetadataTools.class);
|
||||
List<MetaProperty> properties = ConvertorHelper.getOrderedProperties(metaClass);
|
||||
for (MetaProperty property : properties) {
|
||||
if (metadataTools.isTransient(property))
|
||||
continue;
|
||||
|
||||
if (!attrViewPermitted(metaClass, property.getName()))
|
||||
continue;
|
||||
|
||||
Object value = entity.getValue(property.getName());
|
||||
if (property.getAnnotatedElement().isAnnotationPresent(Id.class)) {
|
||||
//skipping: we encoded it before
|
||||
continue;
|
||||
}
|
||||
switch (property.getType()) {
|
||||
case DATATYPE:
|
||||
root.set(property.getName(), property.getRange().asDatatype().format(value));
|
||||
break;
|
||||
case ENUM:
|
||||
root.set(property.getName(), property.getRange().asEnumeration().format(value));
|
||||
break;
|
||||
case COMPOSITION:
|
||||
case ASSOCIATION: {
|
||||
MetaClass meta = propertyMetaClass(property);
|
||||
//checks if the user permitted to read a property's entity
|
||||
if (!readPermitted(meta))
|
||||
break;
|
||||
|
||||
if (!property.getRange().getCardinality().isMany()) {
|
||||
if (value == null) {
|
||||
root.set(property.getName(), null);
|
||||
} else {
|
||||
root.set(property.getName(), encodeInstance((Entity) value, visited,
|
||||
property.getRange().asClass()));
|
||||
}
|
||||
} else {
|
||||
if (value == null) {
|
||||
root.set(property.getName(), null);
|
||||
break;
|
||||
}
|
||||
|
||||
MyJSONObject.Array array = new MyJSONObject.Array();
|
||||
root.set(property.getName(), array);
|
||||
|
||||
Collection<?> members = (Collection<?>) value;
|
||||
for (Object o : members) {
|
||||
if (o == null) {
|
||||
array.add(null);
|
||||
} else {
|
||||
array.add(encodeInstance((Entity) o, visited,
|
||||
property.getRange().asClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Unknown property type");
|
||||
}
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private String idof(Entity entity) {
|
||||
return EntityLoadInfo.create(entity).toString();
|
||||
}
|
||||
|
||||
private boolean attrViewPermitted(MetaClass metaClass, String property) {
|
||||
return attrPermitted(metaClass, property, EntityAttrAccess.VIEW);
|
||||
}
|
||||
|
||||
private boolean attrModifyPermitted(MetaClass metaClass, String property) {
|
||||
return attrPermitted(metaClass, property, EntityAttrAccess.MODIFY);
|
||||
}
|
||||
|
||||
private boolean attrPermitted(MetaClass metaClass, String property, EntityAttrAccess entityAttrAccess) {
|
||||
UserSession session = UserSessionProvider.getUserSession();
|
||||
return session.isEntityAttrPermitted(metaClass, property, entityAttrAccess);
|
||||
}
|
||||
|
||||
private boolean readPermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.READ);
|
||||
}
|
||||
|
||||
private boolean updatePermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.UPDATE);
|
||||
}
|
||||
|
||||
private boolean entityOpPermitted(MetaClass metaClass, EntityOp entityOp) {
|
||||
UserSession session = UserSessionProvider.getUserSession();
|
||||
return session.isEntityOpPermitted(metaClass, entityOp);
|
||||
}
|
||||
|
||||
private MetaClass propertyMetaClass(MetaProperty property) {
|
||||
return property.getRange().asClass();
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.cuba.core.global.AppBeans;
|
||||
import com.haulmont.cuba.core.global.PasswordEncryption;
|
||||
import com.haulmont.cuba.security.app.LoginWorker;
|
||||
import com.haulmont.cuba.security.global.LoginException;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import javax.activation.MimeTypeParseException;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author chevelev
|
||||
* @version $Id$
|
||||
*/
|
||||
@Controller
|
||||
@RequestMapping(value = "/login")
|
||||
public class LoginServiceController {
|
||||
|
||||
@Inject
|
||||
private PasswordEncryption passwordEncryption;
|
||||
|
||||
private static Log log = LogFactory.getLog(LoginServiceController.class);
|
||||
private static MimeType FORM_TYPE;
|
||||
|
||||
static {
|
||||
try {
|
||||
FORM_TYPE = new MimeType("application/x-www-form-urlencoded;charset=UTF-8");
|
||||
} catch (MimeTypeParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.POST)
|
||||
public void loginByPost(@RequestBody String requestBody,
|
||||
@RequestHeader(value = "Content-Type") MimeType contentType,
|
||||
HttpServletResponse response) throws IOException, JSONException {
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
String username;
|
||||
String password;
|
||||
Locale locale;
|
||||
if (contentType.match(JSONConvertor.MIME_TYPE_JSON)) {
|
||||
JSONObject json = new JSONObject(requestBody);
|
||||
username = json.getString("username");
|
||||
password = json.getString("password");
|
||||
locale = new Locale(json.getString("locale"));
|
||||
} else if (contentType.match(FORM_TYPE)) {
|
||||
String[] pairs = requestBody.split("\\&");
|
||||
Map<String, String> name2value = new HashMap<>();
|
||||
for (String pair : pairs) {
|
||||
String[] fields = pair.split("=");
|
||||
String name = URLDecoder.decode(fields[0], "UTF-8");
|
||||
String value = URLDecoder.decode(fields[1], "UTF-8");
|
||||
name2value.put(name, value);
|
||||
}
|
||||
username = name2value.get("username");
|
||||
password = name2value.get("password");
|
||||
locale = new Locale(name2value.get("locale"));
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported content type: " + contentType);
|
||||
}
|
||||
|
||||
try {
|
||||
LoginWorker svc = AppBeans.get(LoginWorker.NAME);
|
||||
UserSession userSession = svc.login(username, passwordEncryption.getPlainHash(password), locale);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
PrintWriter writer = new PrintWriter(response.getOutputStream());
|
||||
writer.write(userSession.getId().toString());
|
||||
writer.close();
|
||||
|
||||
log.debug(String.format("User %s logged in with REST-API, session id: %s", username, userSession.getId()));
|
||||
} catch (LoginException e) {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET)
|
||||
public void loginByGet(@RequestParam(value = "u") String username,
|
||||
@RequestParam(value = "p") String password,
|
||||
@RequestParam(value = "l") String localeStr,
|
||||
HttpServletResponse response) throws IOException, JSONException {
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
Locale locale = StringUtils.isBlank(localeStr) ? new Locale("en") : new Locale(localeStr);
|
||||
try {
|
||||
LoginWorker svc = AppBeans.get(LoginWorker.NAME);
|
||||
|
||||
UserSession userSession = svc.login(username, passwordEncryption.getPlainHash(password), locale);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
PrintWriter writer = new PrintWriter(response.getOutputStream());
|
||||
writer.write(userSession.getId().toString());
|
||||
writer.close();
|
||||
|
||||
log.debug(String.format("User %s logged in with REST-API, session id: %s", username, userSession.getId()));
|
||||
} catch (LoginException e) {
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
public interface MyJSON {
|
||||
/**
|
||||
* Render into a string buffer.
|
||||
*
|
||||
* @param level level at which this instance is being rendered
|
||||
* @return a mutable buffer
|
||||
*/
|
||||
public StringBuilder asString(int level);
|
||||
|
||||
public static final char FIELD_SEPARATOR = ',';
|
||||
public static final char MEMBER_SEPARATOR = ',';
|
||||
public static final char VALUE_SEPARATOR = ':';
|
||||
public static final char IOR_SEPARTOR = '-';
|
||||
public static final char QUOTE = '"';
|
||||
public static final char SPACE = ' ';
|
||||
public static final char OBJECT_START = '{';
|
||||
public static final char OBJECT_END = '}';
|
||||
public static final char ARRAY_START = '[';
|
||||
public static final char ARRAY_END = ']';
|
||||
|
||||
public static final String NEWLINE = "\r\n";
|
||||
public static final String NULL_LITERAL = "null";
|
||||
public static final String REF_MARKER = "ref";
|
||||
public static final String ID_MARKER = "id";
|
||||
public static final String ARRAY_EMPTY = "[]";
|
||||
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.*;
|
||||
|
||||
public class MyJSONObject implements MyJSON {
|
||||
private final String _id;
|
||||
private final boolean _ref;
|
||||
private final Map<String, Object> _values;
|
||||
|
||||
public MyJSONObject(Object id, boolean ref) {
|
||||
_id = id.toString();
|
||||
_ref = ref;
|
||||
_values = new LinkedHashMap<String, Object>();
|
||||
}
|
||||
|
||||
public void set(String key, Object value) {
|
||||
_values.put(key, value);
|
||||
}
|
||||
|
||||
public void write(PrintWriter writer) {
|
||||
writer.println(toString());
|
||||
}
|
||||
public String toString() {
|
||||
return asString(0).toString();
|
||||
}
|
||||
|
||||
public StringBuilder asString(int indent) {
|
||||
StringBuilder buf = new StringBuilder().append(OBJECT_START);
|
||||
buf.append(encodeField(_ref ? REF_MARKER : ID_MARKER, ior(), 0));
|
||||
if (_ref) {
|
||||
return buf.append(OBJECT_END);
|
||||
}
|
||||
StringBuilder tab = newIndent(indent+1);
|
||||
for (Map.Entry<String, Object> e : _values.entrySet()) {
|
||||
buf.append(FIELD_SEPARATOR).append(NEWLINE);
|
||||
buf.append(tab).append(encodeField(e.getKey(), e.getValue(), indent+1));
|
||||
}
|
||||
buf.append(NEWLINE)
|
||||
.append(newIndent(indent))
|
||||
.append(OBJECT_END);
|
||||
return buf;
|
||||
}
|
||||
|
||||
private static StringBuilder encodeField(String field, Object value, int indent) {
|
||||
return new StringBuilder()
|
||||
.append(quoteFieldName(field))
|
||||
.append(VALUE_SEPARATOR)
|
||||
.append(quoteFieldValue(value, indent));
|
||||
}
|
||||
|
||||
private static StringBuilder newIndent(int indent) {
|
||||
char[] tabs = new char[indent*4];
|
||||
Arrays.fill(tabs, SPACE);
|
||||
return new StringBuilder().append(tabs);
|
||||
}
|
||||
|
||||
|
||||
String ior() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
private static StringBuilder quoteFieldName(String s) {
|
||||
return new StringBuilder().append(QUOTE).append(s).append(QUOTE);
|
||||
}
|
||||
|
||||
private static StringBuilder quoteFieldValue(Object o, int indent) {
|
||||
if (o == null) return new StringBuilder(NULL_LITERAL);
|
||||
if (o instanceof Number) return new StringBuilder(o.toString());
|
||||
if (o instanceof MyJSON) return ((MyJSON)o).asString(indent);
|
||||
return quoted(o.toString());
|
||||
}
|
||||
|
||||
private static StringBuilder quoted(Object o) {
|
||||
if (o == null) return new StringBuilder(NULL_LITERAL);
|
||||
String escaped = o.toString().replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
.replace("\r", "\\r")
|
||||
.replace("\n", "\\n");
|
||||
return new StringBuilder().append(QUOTE).append(escaped).append(QUOTE);
|
||||
}
|
||||
|
||||
public static class Array implements MyJSON {
|
||||
private List<Object> _members = new ArrayList<Object>();
|
||||
|
||||
public void add(Object o) {
|
||||
_members.add(o);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return asString(0).toString();
|
||||
}
|
||||
|
||||
public StringBuilder asString(int indent) {
|
||||
StringBuilder buf = new StringBuilder().append(ARRAY_START);
|
||||
StringBuilder tab = MyJSONObject.newIndent(indent+1);
|
||||
for (Object o : _members) {
|
||||
if (buf.length() > 1) buf.append(MEMBER_SEPARATOR);
|
||||
buf.append(NEWLINE).append(tab);
|
||||
if (o instanceof MyJSON)
|
||||
buf.append(((MyJSON)o).asString(indent+1));
|
||||
else
|
||||
buf.append(o);
|
||||
}
|
||||
buf.append(NEWLINE)
|
||||
.append(MyJSONObject.newIndent(indent))
|
||||
.append(ARRAY_END);
|
||||
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
public static class KVMap implements MyJSON {
|
||||
private Map<Object,Object> _entries = new LinkedHashMap<Object,Object>();
|
||||
|
||||
public void put(Object k, Object v) {
|
||||
_entries.put(k,v);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return asString(0).toString();
|
||||
}
|
||||
|
||||
public StringBuilder asString(int indent) {
|
||||
StringBuilder buf = new StringBuilder().append(ARRAY_START);
|
||||
StringBuilder tab = MyJSONObject.newIndent(indent+1);
|
||||
for (Map.Entry<Object, Object> e : _entries.entrySet()) {
|
||||
if (buf.length() > 1) buf.append(MEMBER_SEPARATOR);
|
||||
buf.append(NEWLINE).append(tab);
|
||||
Object key = e.getKey();
|
||||
if (key instanceof MyJSON) {
|
||||
buf.append(((MyJSON)key).asString(indent+1));
|
||||
} else {
|
||||
buf.append(key);
|
||||
}
|
||||
buf.append(VALUE_SEPARATOR);
|
||||
Object value = e.getValue();
|
||||
if (value instanceof MyJSON) {
|
||||
buf.append(((MyJSON)value).asString(indent+2));
|
||||
} else {
|
||||
buf.append(value);
|
||||
}
|
||||
|
||||
}
|
||||
buf.append(NEWLINE)
|
||||
.append(MyJSONObject.newIndent(indent))
|
||||
.append(ARRAY_END);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MyJSONObject o = new MyJSONObject("Person-1234", false);
|
||||
MyJSONObject r = new MyJSONObject("Person-1234", true);
|
||||
MyJSONObject f = new MyJSONObject("Person-2345", false);
|
||||
Array a = new Array();
|
||||
a.add(f);
|
||||
a.add(3456);
|
||||
a.add(null);
|
||||
a.add(r);
|
||||
a.add(null);
|
||||
KVMap map = new KVMap();
|
||||
map.put("k1", r);
|
||||
map.put("k2", f);
|
||||
map.put("k3", null);
|
||||
map.put("k4", 3456);
|
||||
map.put(null, 6789);
|
||||
|
||||
f.set("name", "Mary");
|
||||
f.set("age", 30);
|
||||
f.set("friend", r);
|
||||
o.set("name", "John");
|
||||
o.set("age", 20);
|
||||
o.set("friend", f);
|
||||
o.set("friends", a);
|
||||
o.set("map", map);
|
||||
|
||||
System.err.println(o);
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.cuba.core.sys.AppContext;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.text.StrTokenizer;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.servlet.DispatcherServlet;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* <p>$Id$</p>
|
||||
*
|
||||
* @author chevelev
|
||||
*/
|
||||
public class RestApiServlet extends DispatcherServlet {
|
||||
|
||||
public static final String SPRING_CONTEXT_CONFIG = "cuba.restApiSpringContextConfig";
|
||||
private static final long serialVersionUID = -917577256568108225L;
|
||||
|
||||
@Override
|
||||
public String getContextConfigLocation() {
|
||||
String configProperty = AppContext.getProperty(SPRING_CONTEXT_CONFIG);
|
||||
if (StringUtils.isBlank(configProperty)) {
|
||||
throw new IllegalStateException("Missing " + SPRING_CONTEXT_CONFIG + " application property");
|
||||
}
|
||||
File baseDir = new File(AppContext.getProperty("cuba.confDir"));
|
||||
|
||||
StrTokenizer tokenizer = new StrTokenizer(configProperty);
|
||||
String[] tokenArray = tokenizer.getTokenArray();
|
||||
StringBuilder locations = new StringBuilder();
|
||||
for (String token : tokenArray) {
|
||||
String location;
|
||||
if (ResourceUtils.isUrl(token)) {
|
||||
location = token;
|
||||
} else {
|
||||
if (token.startsWith("/"))
|
||||
token = token.substring(1);
|
||||
File file = new File(baseDir, token);
|
||||
if (file.exists()) {
|
||||
location = file.toURI().toString();
|
||||
} else {
|
||||
location = "classpath:" + token;
|
||||
}
|
||||
}
|
||||
locations.append(location).append(" ");
|
||||
}
|
||||
return locations.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebApplicationContext initWebApplicationContext() {
|
||||
WebApplicationContext wac = findWebApplicationContext();
|
||||
if (wac == null) {
|
||||
ApplicationContext parent = AppContext.getApplicationContext();
|
||||
wac = createWebApplicationContext(parent);
|
||||
}
|
||||
|
||||
onRefresh(wac);
|
||||
|
||||
// Publish the context as a servlet context attribute.
|
||||
String attrName = getServletContextAttributeName();
|
||||
getServletContext().setAttribute(attrName, wac);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
|
||||
"' as ServletContext attribute with name [" + attrName + "]");
|
||||
}
|
||||
|
||||
return wac;
|
||||
}
|
||||
}
|
@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi;
|
||||
|
||||
import com.haulmont.chile.core.datatypes.impl.StringDatatype;
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.cuba.core.entity.BaseUuidEntity;
|
||||
import com.haulmont.cuba.core.entity.Entity;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.entity.EntityAttrAccess;
|
||||
import com.haulmont.cuba.security.entity.EntityOp;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
import org.w3c.dom.*;
|
||||
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
|
||||
import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSInput;
|
||||
import org.w3c.dom.ls.LSParser;
|
||||
import org.w3c.dom.ls.LSParserFilter;
|
||||
import org.w3c.dom.traversal.NodeFilter;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.ParseException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @version $Id$
|
||||
*/
|
||||
public class XMLConvertor implements Convertor {
|
||||
public static final MimeType MIME_TYPE_XML;
|
||||
public static final String MIME_STR = "text/xml;charset=UTF-8";
|
||||
|
||||
public static final String ELEMENT_INSTANCE = "instance";
|
||||
public static final String ELEMENT_URI = "uri";
|
||||
public static final String ELEMENT_REF = "ref";
|
||||
public static final String ELEMENT_NULL_REF = "null";
|
||||
public static final String ELEMENT_MEMBER = "member";
|
||||
public static final String ATTR_ID = "id";
|
||||
public static final String ATTR_NAME = "name";
|
||||
public static final String ATTR_VERSION = "version";
|
||||
public static final String ATTR_NULL = "null";
|
||||
public static final String ATTR_MEMBER_TYPE = "member-type";
|
||||
public static final String NULL_VALUE = "null";
|
||||
|
||||
private static final String EMPTY_TEXT = " ";
|
||||
public static final char DASH = '-';
|
||||
public static final char UNDERSCORE = '_';
|
||||
|
||||
public static final String ROOT_ELEMENT_INSTANCE = "instances";
|
||||
|
||||
private static DocumentBuilder _builder;
|
||||
|
||||
public static final String MAPPING_ROOT_ELEMENT_INSTANCE = "mapping";
|
||||
public static final String PAIR_ELEMENT = "pair";
|
||||
|
||||
private static final Transformer _transformer;
|
||||
private static LSParser requestConfigParser;
|
||||
private static DOMImplementationLS lsImpl;
|
||||
|
||||
static {
|
||||
try {
|
||||
MIME_TYPE_XML = new MimeType(MIME_STR);
|
||||
|
||||
_builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
|
||||
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
|
||||
lsImpl = (DOMImplementationLS) registry.getDOMImplementation("LS");
|
||||
requestConfigParser = lsImpl.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS,
|
||||
null);
|
||||
|
||||
// Set options on the parser
|
||||
DOMConfiguration config = requestConfigParser.getDomConfig();
|
||||
config.setParameter("validate", Boolean.TRUE);
|
||||
config.setParameter("element-content-whitespace", Boolean.FALSE);
|
||||
config.setParameter("comments", Boolean.FALSE);
|
||||
requestConfigParser.setFilter(new LSParserFilter() {
|
||||
public short startElement(Element elementArg) {
|
||||
return LSParserFilter.FILTER_ACCEPT;
|
||||
}
|
||||
|
||||
public short acceptNode(Node nodeArg) {
|
||||
return "".equals(nodeArg.getTextContent().trim()) ?
|
||||
LSParserFilter.FILTER_REJECT :
|
||||
LSParserFilter.FILTER_ACCEPT;
|
||||
}
|
||||
|
||||
public int getWhatToShow() {
|
||||
return NodeFilter.SHOW_TEXT;
|
||||
}
|
||||
});
|
||||
|
||||
_transformer = TransformerFactory.newInstance().newTransformer();
|
||||
_transformer.setOutputProperty(OutputKeys.METHOD, "xml");
|
||||
_transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
_transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
|
||||
_transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
||||
_transformer.setOutputProperty(OutputKeys.INDENT, "no");
|
||||
_transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public MimeType getMimeType() {
|
||||
return MIME_TYPE_XML;
|
||||
}
|
||||
|
||||
public Document process(Entity entity, MetaClass metaclass, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
Element root = newDocument(ROOT_ELEMENT_INSTANCE);
|
||||
encodeEntityInstance(new HashSet<Entity>(), entity, root, false, metaclass);
|
||||
Document doc = root.getOwnerDocument();
|
||||
decorate(doc, requestURI);
|
||||
return doc;
|
||||
}
|
||||
|
||||
public Document process(List<Entity> entities, MetaClass metaClass, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
Element root = newDocument(ROOT_ELEMENT_INSTANCE);
|
||||
for (Entity entity : entities) {
|
||||
encodeEntityInstance(new HashSet(), entity, root, false, metaClass);
|
||||
}
|
||||
Document doc = root.getOwnerDocument();
|
||||
decorate(doc, requestURI);
|
||||
return doc;
|
||||
}
|
||||
|
||||
public Object process(Map<Entity, Entity> entityMap, String requestURI)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
Element root = newDocument(MAPPING_ROOT_ELEMENT_INSTANCE);
|
||||
Document doc = root.getOwnerDocument();
|
||||
for (Map.Entry<Entity, Entity> entry : entityMap.entrySet()) {
|
||||
Element pair = doc.createElement(PAIR_ELEMENT);
|
||||
root.appendChild(pair);
|
||||
encodeEntityInstance(
|
||||
new HashSet(), entry.getKey(),
|
||||
pair, false,
|
||||
getMetaClass(entry.getKey())
|
||||
);
|
||||
encodeEntityInstance(
|
||||
new HashSet(), entry.getValue(),
|
||||
pair, false,
|
||||
getMetaClass(entry.getValue())
|
||||
);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public void write(HttpServletResponse response, Object o) throws IOException {
|
||||
Document doc = (Document) o;
|
||||
response.setContentType(MIME_STR);
|
||||
try {
|
||||
_transformer.transform(new DOMSource(doc), new StreamResult(response.getOutputStream()));
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public CommitRequest parseCommitRequest(String content) {
|
||||
try {
|
||||
LSInput lsInput = lsImpl.createLSInput();
|
||||
lsInput.setStringData(content);
|
||||
Document commitRequestDoc = requestConfigParser.parse(lsInput);
|
||||
Node rootNode = commitRequestDoc.getFirstChild();
|
||||
if (!"CommitRequest".equals(rootNode.getNodeName()))
|
||||
throw new IllegalArgumentException("Not a CommitRequest xml passed: " + rootNode.getNodeName());
|
||||
|
||||
CommitRequest result = new CommitRequest();
|
||||
|
||||
NodeList children = rootNode.getChildNodes();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
String childNodeName = child.getNodeName();
|
||||
if ("commitInstances".equals(childNodeName)) {
|
||||
NodeList entitiesNodeList = child.getChildNodes();
|
||||
|
||||
Set<String> commitIds = new HashSet<>(entitiesNodeList.getLength());
|
||||
for (int j = 0; j < entitiesNodeList.getLength(); j++) {
|
||||
Node idNode = entitiesNodeList.item(j).getAttributes().getNamedItem("id");
|
||||
if (idNode == null)
|
||||
continue;
|
||||
|
||||
String id = idNode.getTextContent();
|
||||
if (id.startsWith("NEW-"))
|
||||
id = id.substring(id.indexOf('-') + 1);
|
||||
commitIds.add(id);
|
||||
}
|
||||
|
||||
result.setCommitIds(commitIds);
|
||||
result.setCommitInstances(parseNodeList(result, entitiesNodeList));
|
||||
} else if ("removeInstances".equals(childNodeName)) {
|
||||
NodeList entitiesNodeList = child.getChildNodes();
|
||||
|
||||
List removeInstances = parseNodeList(result, entitiesNodeList);
|
||||
result.setRemoveInstances(removeInstances);
|
||||
} else if ("softDeletion".equals(childNodeName)) {
|
||||
result.setSoftDeletion(Boolean.parseBoolean(child.getTextContent()));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IntrospectionException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List parseNodeList(CommitRequest commitRequest, NodeList entitiesNodeList) throws InstantiationException, IllegalAccessException, InvocationTargetException, IntrospectionException, ParseException {
|
||||
List entities = new ArrayList(entitiesNodeList.getLength());
|
||||
for (int j = 0; j < entitiesNodeList.getLength(); j++) {
|
||||
Node entityNode = entitiesNodeList.item(j);
|
||||
if (ELEMENT_INSTANCE.equals(entityNode.getNodeName())) {
|
||||
InstanceRef ref = commitRequest.parseInstanceRefAndRegister(getIdAttribute(entityNode));
|
||||
MetaClass metaClass = ref.getMetaClass();
|
||||
Object instance = ref.getInstance();
|
||||
parseEntity(commitRequest, instance, metaClass, entityNode);
|
||||
entities.add(instance);
|
||||
}
|
||||
}
|
||||
return entities;
|
||||
}
|
||||
|
||||
private void parseEntity(CommitRequest commitRequest, Object bean, MetaClass metaClass, Node node)
|
||||
throws InstantiationException, IllegalAccessException, InvocationTargetException, IntrospectionException, ParseException {
|
||||
MetadataTools metadataTools = AppBeans.get(MetadataTools.class);
|
||||
NodeList fields = node.getChildNodes();
|
||||
for (int i = 0; i < fields.getLength(); i++) {
|
||||
Node fieldNode = fields.item(i);
|
||||
String fieldName = getFieldName(fieldNode);
|
||||
MetaProperty property = metaClass.getProperty(fieldName);
|
||||
|
||||
if (!attrModifyPermitted(metaClass, property.getName()))
|
||||
continue;
|
||||
|
||||
if (metadataTools.isTransient(bean, fieldName))
|
||||
continue;
|
||||
|
||||
String xmlValue = fieldNode.getTextContent();
|
||||
if (isNullValue(fieldNode)) {
|
||||
setNullField(bean, fieldName);
|
||||
}
|
||||
|
||||
Object value;
|
||||
|
||||
switch (property.getType()) {
|
||||
case DATATYPE:
|
||||
case ENUM:
|
||||
if (property.getAnnotatedElement().isAnnotationPresent(Id.class)) {
|
||||
// it was parsed in the beginning
|
||||
continue;
|
||||
}
|
||||
|
||||
String typeName = property.getRange().<Object>asDatatype().getName();
|
||||
|
||||
if (property.getType() == MetaProperty.Type.DATATYPE)
|
||||
if (!StringDatatype.NAME.equals(typeName) && "null".equals(xmlValue))
|
||||
value = null;
|
||||
else
|
||||
value = property.getRange().<Object>asDatatype().parse(xmlValue);
|
||||
else
|
||||
value = property.getRange().asEnumeration().parse(xmlValue);
|
||||
|
||||
setField(bean, fieldName, value);
|
||||
break;
|
||||
case COMPOSITION:
|
||||
case ASSOCIATION: {
|
||||
if ("null".equals(xmlValue)) {
|
||||
setField(bean, fieldName, null);
|
||||
break;
|
||||
}
|
||||
MetaClass propertyMetaClass = propertyMetaClass(property);
|
||||
//checks if the user permitted to read and update a property
|
||||
if (!updatePermitted(propertyMetaClass) && !readPermitted(propertyMetaClass))
|
||||
break;
|
||||
|
||||
if (!property.getRange().getCardinality().isMany()) {
|
||||
if (property.getAnnotatedElement().isAnnotationPresent(Embedded.class)) {
|
||||
MetaClass embeddedMetaClass = property.getRange().asClass();
|
||||
value = embeddedMetaClass.createInstance();
|
||||
parseEntity(commitRequest, value, embeddedMetaClass, fieldNode);
|
||||
} else {
|
||||
String id = getRefId(fieldNode);
|
||||
|
||||
//reference to an entity that also a commit instance
|
||||
//will be registered later
|
||||
if (commitRequest.getCommitIds().contains(id)) {
|
||||
EntityLoadInfo loadInfo = EntityLoadInfo.parse(id);
|
||||
BaseUuidEntity ref = loadInfo.getMetaClass().createInstance();
|
||||
ref.setValue("id", loadInfo.getId());
|
||||
setField(bean, fieldName, ref);
|
||||
break;
|
||||
}
|
||||
|
||||
value = parseEntityReference(fieldNode, commitRequest);
|
||||
}
|
||||
setField(bean, fieldName, value);
|
||||
} else {
|
||||
NodeList memberNodes = fieldNode.getChildNodes();
|
||||
Collection<Object> members = property.getRange().isOrdered() ? new ArrayList<Object>() : new HashSet<Object>();
|
||||
for (int memberIndex = 0; memberIndex < memberNodes.getLength(); memberIndex++) {
|
||||
Node memberNode = memberNodes.item(memberIndex);
|
||||
members.add(parseEntityReference(memberNode, commitRequest));
|
||||
}
|
||||
setField(bean, fieldName, members);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Unknown property type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getRefId(Node refNode) {
|
||||
Node childNode = refNode.getFirstChild();
|
||||
do {
|
||||
if (ELEMENT_REF.equals(childNode.getNodeName())) {
|
||||
Node idNode = childNode.getAttributes().getNamedItem(ATTR_ID);
|
||||
return idNode != null ? idNode.getTextContent() : null;
|
||||
}
|
||||
childNode = childNode.getNextSibling();
|
||||
} while (childNode != null);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object parseEntityReference(Node node, CommitRequest commitRequest)
|
||||
throws InstantiationException, IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
Node childNode = node.getFirstChild();
|
||||
if (ELEMENT_NULL_REF.equals(childNode.getNodeName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InstanceRef ref = commitRequest.parseInstanceRefAndRegister(getIdAttribute(childNode));
|
||||
return ref.getInstance();
|
||||
}
|
||||
|
||||
private void setField(Object result, String fieldName, Object value) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
new PropertyDescriptor(fieldName, result.getClass()).
|
||||
getWriteMethod().invoke(result, value);
|
||||
}
|
||||
|
||||
private void setNullField(Object bean, String fieldName) throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
setField(bean, fieldName, null);
|
||||
}
|
||||
|
||||
private String getFieldName(Node fieldNode) {
|
||||
return getAttributeValue(fieldNode, ATTR_NAME);
|
||||
}
|
||||
|
||||
private String getIdAttribute(Node node) {
|
||||
return getAttributeValue(node, ATTR_ID);
|
||||
}
|
||||
|
||||
private String getAttributeValue(Node node, String name) {
|
||||
return node.getAttributes().getNamedItem(name).getNodeValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new document with the given tag as the root element.
|
||||
*
|
||||
* @param rootTag the tag of the root element
|
||||
* @return the document element of a new document
|
||||
*/
|
||||
|
||||
public Element newDocument(String rootTag) {
|
||||
Document doc = _builder.newDocument();
|
||||
Element root = doc.createElement(rootTag);
|
||||
doc.appendChild(root);
|
||||
String[] nvpairs = new String[]{
|
||||
"xmlns:xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI,
|
||||
// "xsi:noNamespaceSchemaLocation", INSTANCE_XSD,
|
||||
ATTR_VERSION, "1.0",
|
||||
};
|
||||
for (int i = 0; i < nvpairs.length; i += 2) {
|
||||
root.setAttribute(nvpairs[i], nvpairs[i + 1]);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
Document decorate(Document doc, String uri) {
|
||||
Element root = doc.getDocumentElement();
|
||||
Element instance = (Element) root.getElementsByTagName(ELEMENT_INSTANCE).item(0);
|
||||
Element uriElement = doc.createElement(ELEMENT_URI);
|
||||
uriElement.setTextContent(uri == null ? NULL_VALUE : uri);
|
||||
root.insertBefore(uriElement, instance);
|
||||
return doc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the closure of a persistent instance into a XML element.
|
||||
*
|
||||
* @param visited
|
||||
* @param entity the managed instance to be encoded. Can be null.
|
||||
* @param parent the parent XML element to which the new XML element be added. Must not be null. Must be
|
||||
* owned by a document.
|
||||
* @param isRef
|
||||
* @param metaClass @return the new element. The element has been appended as a child to the given parent in this method.
|
||||
*/
|
||||
private Element encodeEntityInstance(HashSet<Entity> visited, final Entity entity, final Element parent,
|
||||
boolean isRef, MetaClass metaClass)
|
||||
throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
if (!readPermitted(metaClass))
|
||||
return null;
|
||||
|
||||
if (parent == null)
|
||||
throw new NullPointerException("No parent specified");
|
||||
|
||||
Document doc = parent.getOwnerDocument();
|
||||
if (doc == null)
|
||||
throw new NullPointerException("No document specified");
|
||||
|
||||
if (entity == null) {
|
||||
return encodeRef(parent, entity);
|
||||
}
|
||||
|
||||
isRef |= !visited.add(entity);
|
||||
|
||||
if (isRef) {
|
||||
return encodeRef(parent, entity);
|
||||
}
|
||||
Element root = doc.createElement(ELEMENT_INSTANCE);
|
||||
parent.appendChild(root);
|
||||
root.setAttribute(ATTR_ID, ior(entity));
|
||||
|
||||
MetadataTools metadataTools = AppBeans.get(MetadataTools.class);
|
||||
List<MetaProperty> properties = ConvertorHelper.getOrderedProperties(metaClass);
|
||||
for (MetaProperty property : properties) {
|
||||
Element child;
|
||||
if (metadataTools.isTransient(entity, property.getName()))
|
||||
continue;
|
||||
|
||||
if (!attrViewPermitted(metaClass, property.getName()))
|
||||
continue;
|
||||
|
||||
Object value = entity.getValue(property.getName());
|
||||
switch (property.getType()) {
|
||||
case DATATYPE:
|
||||
String nodeType;
|
||||
if (property.getAnnotatedElement().isAnnotationPresent(Id.class)) {
|
||||
continue;
|
||||
} else if (property.getAnnotatedElement().isAnnotationPresent(Version.class)) {
|
||||
nodeType = "version";
|
||||
} else {
|
||||
nodeType = "basic";
|
||||
}
|
||||
child = doc.createElement(nodeType);
|
||||
child.setAttribute(ATTR_NAME, property.getName());
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
} else {
|
||||
String str = property.getRange().<Object>asDatatype().format(value);
|
||||
encodeBasic(child, str, property.getJavaType());
|
||||
}
|
||||
break;
|
||||
case ENUM:
|
||||
child = doc.createElement("enum");
|
||||
child.setAttribute(ATTR_NAME, property.getName());
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
} else {
|
||||
String str = property.getRange().asEnumeration().format(value);
|
||||
encodeBasic(child, str, property.getJavaType());
|
||||
}
|
||||
break;
|
||||
case COMPOSITION:
|
||||
case ASSOCIATION: {
|
||||
MetaClass meta = propertyMetaClass(property);
|
||||
//checks if the user permitted to read a property
|
||||
if (!readPermitted(meta)) {
|
||||
child = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!property.getRange().getCardinality().isMany()) {
|
||||
boolean isEmbedded = property.getAnnotatedElement().isAnnotationPresent(Embedded.class);
|
||||
child = doc.createElement(isEmbedded ?
|
||||
"embedded" :
|
||||
property.getRange().getCardinality().name().replace(UNDERSCORE, DASH).toLowerCase()
|
||||
);
|
||||
child.setAttribute(ATTR_NAME, property.getName());
|
||||
if (isEmbedded) {
|
||||
encodeEntityInstance(visited, (Entity) value, child, false, property.getRange().asClass());
|
||||
} else {
|
||||
encodeEntityInstance(visited, (Entity) value, child, false, property.getRange().asClass());
|
||||
}
|
||||
} else {
|
||||
child = doc.createElement(getCollectionReferenceTag(property));
|
||||
child.setAttribute(ATTR_NAME, property.getName());
|
||||
child.setAttribute(ATTR_MEMBER_TYPE, typeOfEntityProperty(property));
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
break;
|
||||
}
|
||||
Collection<?> members = (Collection<?>) value;
|
||||
for (Object o : members) {
|
||||
Element member = doc.createElement(ELEMENT_MEMBER);
|
||||
child.appendChild(member);
|
||||
if (o == null) {
|
||||
encodeNull(member);
|
||||
} else {
|
||||
encodeEntityInstance(visited, (Entity) o, member, true, property.getRange().asClass());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException("Unknown property type");
|
||||
}
|
||||
|
||||
if (child != null) {
|
||||
root.appendChild(child);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
private String typeOfEntityProperty(MetaProperty property) {
|
||||
return property.getRange().asClass().getName();
|
||||
}
|
||||
|
||||
private MetaClass propertyMetaClass(MetaProperty property) {
|
||||
return property.getRange().asClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given value element as null. The <code>null</code> attribute is set to true.
|
||||
*
|
||||
* @param element the XML element to be set
|
||||
*/
|
||||
private void encodeNull(Element element) {
|
||||
element.setAttribute(ATTR_NULL, "true");
|
||||
}
|
||||
|
||||
private boolean isNullValue(Node fieldNode) {
|
||||
Node nullAttr = fieldNode.getAttributes().getNamedItem(ATTR_NULL);
|
||||
return nullAttr == null ?
|
||||
false :
|
||||
"true".equals(nullAttr.getNodeValue());
|
||||
}
|
||||
|
||||
private Element encodeRef(Element parent, Entity entity) {
|
||||
Element ref = parent.getOwnerDocument().createElement(entity == null ? ELEMENT_NULL_REF : ELEMENT_REF);
|
||||
if (entity != null)
|
||||
ref.setAttribute(ATTR_ID, ior(entity));
|
||||
|
||||
// IMPORTANT: for xml transformer not to omit the closing tag, otherwise dojo is confused
|
||||
ref.setTextContent(EMPTY_TEXT);
|
||||
parent.appendChild(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the given value element. The <code>type</code> is set to the given runtime type.
|
||||
* String form of the given object is set as the text content.
|
||||
*
|
||||
* @param element the XML element to be set
|
||||
* @param obj value of the element. Never null.
|
||||
* @param runtimeType attribute type
|
||||
*/
|
||||
private void encodeBasic(Element element, Object obj, Class<?> runtimeType) {
|
||||
element.setTextContent(obj == null ? NULL_VALUE : obj.toString());
|
||||
}
|
||||
|
||||
String ior(Entity entity) {
|
||||
return EntityLoadInfo.create(entity).toString();
|
||||
}
|
||||
|
||||
String typeOf(Class<?> cls) {
|
||||
return cls.getSimpleName();
|
||||
}
|
||||
|
||||
private String getCollectionReferenceTag(MetaProperty property) {
|
||||
return property.getRange().getCardinality().name().replace(UNDERSCORE, DASH).toLowerCase();
|
||||
}
|
||||
|
||||
private MetaClass getMetaClass(Entity entity) {
|
||||
return MetadataProvider.getSession().getClass(entity.getClass());
|
||||
}
|
||||
|
||||
private boolean attrViewPermitted(MetaClass metaClass, String property) {
|
||||
return attrPermitted(metaClass, property, EntityAttrAccess.VIEW);
|
||||
}
|
||||
|
||||
private boolean attrModifyPermitted(MetaClass metaClass, String property) {
|
||||
return attrPermitted(metaClass, property, EntityAttrAccess.MODIFY);
|
||||
}
|
||||
|
||||
private boolean attrPermitted(MetaClass metaClass, String property, EntityAttrAccess entityAttrAccess) {
|
||||
UserSession session = UserSessionProvider.getUserSession();
|
||||
return session.isEntityAttrPermitted(metaClass, property, entityAttrAccess);
|
||||
}
|
||||
|
||||
private boolean readPermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.READ);
|
||||
}
|
||||
|
||||
private boolean updatePermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.UPDATE);
|
||||
}
|
||||
|
||||
private boolean entityOpPermitted(MetaClass metaClass, EntityOp entityOp) {
|
||||
UserSession session = UserSessionProvider.getUserSession();
|
||||
return session.isEntityOpPermitted(metaClass, entityOp);
|
||||
}
|
||||
|
||||
}
|
143
modules/portal/src/com/haulmont/cuba/portal/restapi/instance.xsd
Normal file
143
modules/portal/src/com/haulmont/cuba/portal/restapi/instance.xsd
Normal file
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!--
|
||||
~ Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
~ Haulmont Technology proprietary and confidential.
|
||||
~ Use is subject to license terms.
|
||||
-->
|
||||
<!-- ========================================================================= -->
|
||||
<!-- Schema for serialized persistence instance. -->
|
||||
<!-- ========================================================================= -->
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
attributeFormDefault="unqualified" elementFormDefault="qualified"
|
||||
version="1.0">
|
||||
|
||||
<!-- The root element of the document contains zero or more instances -->
|
||||
<xsd:element name="instances">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="uri" minOccurs="1" maxOccurs="1" type="xsd:anyURI"/>
|
||||
<xsd:element name="description" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||
<xsd:element name="instance" minOccurs="0" maxOccurs="unbounded" type="instance-type" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="version" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<!-- The root element for a single instance. Children of this element are persistent attribute -->
|
||||
<!-- Persistent Attributes occur in order. The order is determined by the attribute category. -->
|
||||
<!-- Attribute category is determined by the enumerated PersistentAttributeType defined in -->
|
||||
<!-- javax.persistence.metamodel and then further refined by id, version, lob and enum. -->
|
||||
<xsd:complexType name="instance-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="id" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="version" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="basic" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="enum" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="embedded" type="instance-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="lob" type="lob-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="one-to-one" type="singular-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="many-to-one" type="singular-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="element-collection" type="collection-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="one-to-many" type="collection-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="many-to-many" type="map-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- A reference to another instance within the same(?) document -->
|
||||
<xsd:complexType name="ref-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="id" type="xsd:IDREF" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- A null reference -->
|
||||
<xsd:complexType name="ref-null">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Basic Attribute has a name and its runtime type -->
|
||||
<!-- non-null value appears as text content. -->
|
||||
<!-- null value appears as attribute with empty text . -->
|
||||
<xsd:complexType name="basic-attr-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="null" type="xsd:boolean" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Large Binary Objects (LOB) represented as hex array -->
|
||||
<xsd:complexType name="lob-attr-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:hexBinary">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="null" type="xsd:boolean" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Singular attribute is a reference to another instance or a null reference. -->
|
||||
<xsd:complexType name="singular-attr-type">
|
||||
<xsd:choice>
|
||||
<xsd:element name="null" type="ref-null" />
|
||||
<xsd:element name="ref" type="ref-type" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Collection attributes list their members with their runtime type -->
|
||||
<!-- Members can be basic or other managed instance -->
|
||||
<xsd:complexType name="collection-attr-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="member" type="member-type" minOccurs="0"
|
||||
maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="member-type" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Map attributes list their entries with runtime type of key and value -->
|
||||
<!-- Both key and value can be independently basic or other managed instance -->
|
||||
<xsd:complexType name="map-attr-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="entry" type="entry-type" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="key-type" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="value-type" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Value of a member of basic type. -->
|
||||
<xsd:complexType name="basic-value-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="null" type="xsd:boolean" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Value of a member of a collection/map -->
|
||||
<xsd:complexType name="member-type">
|
||||
<xsd:choice>
|
||||
<xsd:element name="basic" type="basic-value-type" />
|
||||
<xsd:element name="null" type="ref-null" />
|
||||
<xsd:element name="ref" type="ref-type" />
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Denotes entry of a map element -->
|
||||
<xsd:complexType name="entry-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="key" type="member-type" minOccurs="1" maxOccurs="1" />
|
||||
<xsd:element name="value" type="member-type" minOccurs="1" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
* Haulmont Technology proprietary and confidential.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
package com.haulmont.cuba.core.sys.restapi.template;
|
||||
|
||||
import com.haulmont.chile.core.model.MetaClass;
|
||||
import com.haulmont.chile.core.model.MetaProperty;
|
||||
import com.haulmont.cuba.core.global.*;
|
||||
import com.haulmont.cuba.security.entity.EntityAttrAccess;
|
||||
import com.haulmont.cuba.security.entity.EntityOp;
|
||||
import com.haulmont.cuba.security.global.UserSession;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Author: Alexander Chevelev
|
||||
* Date: 26.05.2011
|
||||
* Time: 0:11:34
|
||||
*
|
||||
* @version $Id$
|
||||
*/
|
||||
public class MetaClassRepresentation {
|
||||
private MetaClass meta;
|
||||
private List<View> views;
|
||||
|
||||
public MetaClassRepresentation(MetaClass meta, List<View> views) {
|
||||
this.meta = meta;
|
||||
this.views = views;
|
||||
getTableName();
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
boolean isEmbeddable = meta.getJavaClass().isAnnotationPresent(Embeddable.class);
|
||||
if (isEmbeddable)
|
||||
return "not defined for embeddable entities";
|
||||
|
||||
Table tableAnn = (Table) meta.getJavaClass().getAnnotation(Table.class);
|
||||
return tableAnn != null ? tableAnn.name() : "not defined";
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return meta.getName();
|
||||
}
|
||||
|
||||
public String getParent() {
|
||||
MetaClass ancestor = meta.getAncestor();
|
||||
|
||||
if (ancestor == null || !ancestor.getName().contains("$") ||
|
||||
ancestor.getJavaClass().isAnnotationPresent(MappedSuperclass.class))
|
||||
return "";
|
||||
|
||||
if (!readPermitted(ancestor)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "Parent is " + asHref(ancestor);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
String result = AppBeans.get(MessageTools.class).getEntityCaption(meta);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
|
||||
public Collection<MetaClassRepProperty> getProperties() {
|
||||
List<MetaClassRepProperty> result = new ArrayList<MetaClassRepProperty>();
|
||||
for (MetaProperty property : meta.getProperties()) {
|
||||
MetaProperty.Type propertyType = property.getType();
|
||||
//don't show property if user don't have permissions to view it
|
||||
if (!attrViewPermitted(meta, property.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//don't show property if it's reference and user
|
||||
//don't have permissions to view it's entity class
|
||||
if (propertyType == MetaProperty.Type.COMPOSITION
|
||||
|| propertyType == MetaProperty.Type.ASSOCIATION) {
|
||||
MetaClass propertyMetaClass = propertyMetaClass(property);
|
||||
if (!readPermitted(propertyMetaClass))
|
||||
continue;
|
||||
}
|
||||
MetaClassRepProperty prop = new MetaClassRepProperty(property);
|
||||
result.add(prop);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class MetaClassRepProperty {
|
||||
private MetaProperty property;
|
||||
public MetaClassRepProperty(MetaProperty property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
Column column = property.getAnnotatedElement().getAnnotation(Column.class);
|
||||
if (column != null)
|
||||
return column.name();
|
||||
|
||||
JoinColumn joinColumn = property.getAnnotatedElement().getAnnotation(JoinColumn.class);
|
||||
return joinColumn != null ? joinColumn.name() : "";
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return property.getName();
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
String result = AppBeans.get(MessageTools.class).getPropertyCaption(property);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
|
||||
public String getEnum() {
|
||||
return property.getRange().isEnum() ? "(enum)" : "";
|
||||
}
|
||||
|
||||
public String getJavaType() {
|
||||
String type = property.getJavaType().getName();
|
||||
String simpleName = property.getJavaType().getSimpleName();
|
||||
return type.startsWith("java.lang.") && ("java.lang.".length() + simpleName.length() == type.length()) ?
|
||||
simpleName :
|
||||
property.getRange().isClass() ?
|
||||
asHref(property.getRange().asClass()) :
|
||||
type;
|
||||
}
|
||||
|
||||
public String getCardinality() {
|
||||
switch (property.getRange().getCardinality()) {
|
||||
case ONE_TO_ONE:
|
||||
return property.getRange().isClass() ? "1:1" : "";
|
||||
case ONE_TO_MANY:
|
||||
return "1:N";
|
||||
case MANY_TO_ONE:
|
||||
return "N:1";
|
||||
case MANY_TO_MANY:
|
||||
return "N:N";
|
||||
default:
|
||||
return property.getRange().getCardinality().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public String getOrdered() {
|
||||
return property.getRange().isOrdered() ? "Ordered" : "";
|
||||
}
|
||||
|
||||
public String getMandatory() {
|
||||
return property.isMandatory() ? "Mandatory" : "Optional";
|
||||
}
|
||||
|
||||
public String getReadOnly() {
|
||||
return property.isReadOnly() ? "Read Only" : "Read/Write";
|
||||
}
|
||||
|
||||
public Collection<String> getAnnotations() {
|
||||
Collection<String> result = new ArrayList<String>();
|
||||
Map<String, Object> map = property.getAnnotations();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
String annotationName = entry.getKey();
|
||||
if ("length".equals(annotationName) && !String.class.equals(property.getJavaType()))
|
||||
continue;
|
||||
|
||||
result.add(annotationName + ": " + entry.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isPersistent() {
|
||||
return AppBeans.get(MetadataTools.class).isPersistent(property);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MetaClassRepView> getViews() {
|
||||
if (views == null)
|
||||
return null;
|
||||
|
||||
Collection<MetaClassRepView> result = new ArrayList<MetaClassRepView>();
|
||||
for (View view : views) {
|
||||
if (!viewAccessPermitted(view))
|
||||
continue;
|
||||
result.add(new MetaClassRepView(view));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean viewAccessPermitted(View view) {
|
||||
Class clazz = view.getEntityClass();
|
||||
MetaClass meta = getMetaClass(clazz);
|
||||
return MetaClassRepresentation.readPermitted(meta);
|
||||
}
|
||||
|
||||
private static MetaClass getMetaClass(Class clazz) {
|
||||
return AppBeans.get(Metadata.class).getSession().getClass(clazz);
|
||||
}
|
||||
|
||||
private static boolean viewPropertyReadPermitted(MetaClass meta, ViewProperty viewProperty) {
|
||||
if (!attrViewPermitted(meta, viewProperty.getName()))
|
||||
return false;
|
||||
|
||||
MetaProperty metaProperty = meta.getProperty(viewProperty.getName());
|
||||
if (metaProperty.getType() == MetaProperty.Type.DATATYPE
|
||||
|| metaProperty.getType() == MetaProperty.Type.ENUM)
|
||||
return true;
|
||||
|
||||
MetaClass propertyMeta = metaProperty.getRange().asClass();
|
||||
return readPermitted(propertyMeta);
|
||||
}
|
||||
|
||||
public static class MetaClassRepView {
|
||||
private View view;
|
||||
|
||||
public MetaClassRepView(View view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return view.getName();
|
||||
}
|
||||
|
||||
public Collection<MetaClassRepViewProperty> getProperties() {
|
||||
Collection<MetaClassRepViewProperty> result = new ArrayList<MetaClassRepViewProperty>();
|
||||
MetaClass meta = getMetaClass(view.getEntityClass());
|
||||
for (ViewProperty property : view.getProperties()) {
|
||||
if (!MetaClassRepresentation.viewPropertyReadPermitted(meta, property))
|
||||
continue;
|
||||
result.add(new MetaClassRepViewProperty(property));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MetaClassRepViewProperty {
|
||||
private ViewProperty property;
|
||||
|
||||
public MetaClassRepViewProperty(ViewProperty property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return property.getName();
|
||||
}
|
||||
|
||||
public String getLazy() {
|
||||
return property.isLazy() ? "LAZY" : "";
|
||||
}
|
||||
|
||||
public MetaClassRepView getView() {
|
||||
return property.getView() == null ? null : new MetaClassRepView(property.getView());
|
||||
}
|
||||
}
|
||||
|
||||
private MetaClass propertyMetaClass(MetaProperty property) {
|
||||
return property.getRange().asClass();
|
||||
}
|
||||
|
||||
private static boolean attrViewPermitted(MetaClass metaClass, String property) {
|
||||
return attrPermitted(metaClass, property, EntityAttrAccess.VIEW);
|
||||
}
|
||||
|
||||
private static boolean attrPermitted(MetaClass metaClass, String property, EntityAttrAccess entityAttrAccess) {
|
||||
UserSession session = AppBeans.get(UserSessionSource.class).getUserSession();
|
||||
return session.isEntityAttrPermitted(metaClass, property, entityAttrAccess);
|
||||
}
|
||||
|
||||
private static boolean readPermitted(MetaClass metaClass) {
|
||||
return entityOpPermitted(metaClass, EntityOp.READ);
|
||||
}
|
||||
|
||||
private static boolean entityOpPermitted(MetaClass metaClass, EntityOp entityOp) {
|
||||
UserSession session = AppBeans.get(UserSessionSource.class).getUserSession();
|
||||
return session.isEntityOpPermitted(metaClass, entityOp);
|
||||
}
|
||||
|
||||
private static String asHref(MetaClass metaClass) {
|
||||
return "<a href=\"#" + metaClass.getName() + "\">" + metaClass.getName() + "</a>";
|
||||
}
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
<!--
|
||||
~ Copyright (c) 2011 Haulmont Technology Ltd. All Rights Reserved.
|
||||
~ Haulmont Technology proprietary and confidential.
|
||||
~ Use is subject to license terms.
|
||||
-->
|
||||
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Data model description</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
</head>
|
||||
|
||||
<style>
|
||||
h1{
|
||||
font-size: 1.75em;
|
||||
}
|
||||
|
||||
table {
|
||||
background-color:#eee;
|
||||
}
|
||||
|
||||
table td {
|
||||
padding: 4px;
|
||||
background-color:white;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table .propertyName{
|
||||
font-weight: bold;
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<#macro printView view>
|
||||
<ul>
|
||||
<#list view.properties as property>
|
||||
<li>${property.name} ${property.lazy}</li>
|
||||
<#if property.view ??>
|
||||
<@printView view = property.view/>
|
||||
</#if>
|
||||
</#list>
|
||||
</ul>
|
||||
</#macro>
|
||||
|
||||
<body style="margin: 40px;">
|
||||
|
||||
<h1>Domain model description</h1>
|
||||
|
||||
<h2>Available basic types:</h2>
|
||||
<ul>
|
||||
<#list availableTypes as type>
|
||||
<li>${type}</li>
|
||||
</#list>
|
||||
</ul>
|
||||
|
||||
<h2>Known entities:</h2>
|
||||
<ul>
|
||||
<#list knownEntities as entity>
|
||||
<li><a href="#${entity.name}">${entity.name} - ${entity.description}</a></li>
|
||||
</#list>
|
||||
</ul>
|
||||
|
||||
|
||||
<#list knownEntities as entity>
|
||||
<a name="${entity.name}"></a>
|
||||
<h2>${entity.name}</h2>
|
||||
<p>Table: ${entity.tableName}</p>
|
||||
<#if entity.parent ??>
|
||||
${entity.parent}
|
||||
</#if>
|
||||
<p>${entity.description}</p>
|
||||
<h3>Fields</h3>
|
||||
<table border="1" bordercolor="lightgray" cellspacing="0" cellpadding="0" width="90%">
|
||||
<col width="15%">
|
||||
<col width="20%">
|
||||
<col width="15%">
|
||||
<col width="25%">
|
||||
<col width="25%">
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Column</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
<th>Cardinality</th>
|
||||
<th>Misc</th>
|
||||
</tr>
|
||||
<#list entity.properties as property>
|
||||
<#if property.persistent>
|
||||
<tr>
|
||||
<td class="propertyName">${property.name}</td>
|
||||
<td>${property.tableName}</td>
|
||||
<td>${property.javaType} ${property.enum}</td>
|
||||
<td>${property.description}</td>
|
||||
<td><i>${property.cardinality} ${property.ordered} ${property.mandatory}. ${property.readOnly}.</i></td>
|
||||
<td>
|
||||
<#list property.annotations as ann>
|
||||
${ann}<br>
|
||||
</#list>
|
||||
</td>
|
||||
</tr>
|
||||
</#if>
|
||||
</#list>
|
||||
</table>
|
||||
|
||||
<#if entity.views ??>
|
||||
<h3>Views</h3>
|
||||
<ul>
|
||||
<#list entity.views as aview>
|
||||
<li><b>${aview.name}</b></li>
|
||||
<@printView view=aview/>
|
||||
</#list>
|
||||
</ul>
|
||||
</#if>
|
||||
<p> </p>
|
||||
</#list>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user