source: ComponentRegistry/trunk/ComponentRegistry/src/main/java/clarin/cmdi/componentregistry/rest/MDValidator.java @ 4550

Last change on this file since 4550 was 4550, checked in by twagoo, 10 years ago

Fixed errors due to schema change allowing only one root element in a profile or component (see r4525)

File size: 9.7 KB
Line 
1package clarin.cmdi.componentregistry.rest;
2
3import clarin.cmdi.componentregistry.ComponentRegistry;
4import clarin.cmdi.componentregistry.ComponentRegistryException;
5import clarin.cmdi.componentregistry.ComponentRegistryResourceResolver;
6import clarin.cmdi.componentregistry.Configuration;
7import clarin.cmdi.componentregistry.MDMarshaller;
8import clarin.cmdi.componentregistry.components.CMDComponentSpec;
9import clarin.cmdi.componentregistry.components.CMDComponentType;
10import clarin.cmdi.componentregistry.model.BaseDescription;
11import clarin.cmdi.componentregistry.model.ComponentDescription;
12import clarin.cmdi.schema.cmd.Validator.Message;
13import clarin.cmdi.schema.cmd.ValidatorException;
14import java.io.ByteArrayInputStream;
15import java.io.ByteArrayOutputStream;
16import java.io.IOException;
17import java.io.InputStream;
18import java.net.MalformedURLException;
19import java.net.URL;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.List;
25import javax.xml.bind.JAXBException;
26import javax.xml.transform.stream.StreamSource;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30public class MDValidator implements Validator {
31   
32    private final static Logger LOG = LoggerFactory.getLogger(MDValidator.class);
33    static final String MISMATCH_ERROR = "Cannot register component as a profile or vica versa.";
34    static final String COMPONENT_NOT_REGISTERED_ERROR = "referenced component is not registered or does not have a correct componentId: ";
35    static final String PARSE_ERROR = "Error in validation input file: ";
36    static final String SCHEMA_ERROR = "Error in reading general component schema: ";
37    static final String IO_ERROR = "Error while reading specification or general component schema: ";
38    static final String COMPONENT_NOT_PUBLICLY_REGISTERED_ERROR = "referenced component cannot be found in the published components: ";
39    static final String COMPONENT_REGISTRY_EXCEPTION_ERROR = "An exception occurred while accessing the component registry: ";
40    static final String ILLEGAL_ATTRIBUTE_NAME_ERROR = "Illegal attribute name: ";
41    static final String UNKNOWN_VALIDATION_ERROR = "Unknown validation error";
42    static final Collection<String> ILLEGAL_ATTRIBUTE_NAMES = Collections.unmodifiableCollection(Arrays.asList("ref", "ComponentId"));
43    private List<String> errorMessages = new ArrayList<String>();
44    private CMDComponentSpec spec = null;
45    private byte[] originalSpecBytes;
46    private final InputStream input;
47    private final BaseDescription description;
48    private final ComponentRegistry registry;
49    private final ComponentRegistry userRegistry;
50    private final ComponentRegistry publicRegistry;
51    private final MDMarshaller marshaller;
52
53    /**
54     *
55     * @param input In order to validate the input is consumed. So use
56     * @see getCMDComponentSpec to get the parsed CMDComponentSpec.
57     * @param desc
58     * @param registry (registry you currently used)
59     * @param userRegistry can be null, We get user registry as well so we can
60     * give nice error messages if needed. Can be the same as
61     * @param registry
62     */
63    public MDValidator(InputStream input, BaseDescription description, ComponentRegistry registry, ComponentRegistry userRegistry, ComponentRegistry publicRegistry, MDMarshaller marshaller) {
64        this.input = input;
65        this.description = description;
66        this.registry = registry;
67        this.userRegistry = userRegistry;
68        this.publicRegistry = publicRegistry;
69        this.marshaller = marshaller;
70    }
71   
72    @Override
73    public List<String> getErrorMessages() {
74        return errorMessages;
75    }
76   
77    @Override
78    public boolean validate() {
79        try {
80            clarin.cmdi.schema.cmd.Validator validator = new clarin.cmdi.schema.cmd.Validator(new URL(Configuration.getInstance().getGeneralComponentSchema()));
81            validator.setResourceResolver(new ComponentRegistryResourceResolver());
82            // We may need to reuse the input stream, so save it to a byte array first
83            originalSpecBytes = getBytesFromInputStream();
84            StreamSource source = new StreamSource(new ByteArrayInputStream(originalSpecBytes));
85            if (!validator.validateProfile(source)) {
86                final List<Message> validatorMessages = validator.getMessages();
87                if (validatorMessages.size() > 0) {
88                    for (Message message : validatorMessages) {
89                        errorMessages.add(PARSE_ERROR + message.getText());
90                    }
91                } else {
92                    errorMessages.add(PARSE_ERROR + UNKNOWN_VALIDATION_ERROR);
93                }
94            } else {
95                spec = unmarshalSpec(originalSpecBytes);
96                if (spec.isIsProfile() != description.isProfile()) {
97                    errorMessages.add(MISMATCH_ERROR);
98                }
99            }
100        } catch (MalformedURLException e) {
101            errorMessages.add(SCHEMA_ERROR + e.getMessage());
102            LOG.error(SCHEMA_ERROR, e);
103        } catch (JAXBException e) {
104            errorMessages.add(PARSE_ERROR + e.getMessage());
105            LOG.error(PARSE_ERROR, e);
106        } catch (ValidatorException e) {
107            errorMessages.add(PARSE_ERROR + e.getMessage());
108            LOG.error(PARSE_ERROR, e);
109        } catch (IOException e) {
110            errorMessages.add(IO_ERROR + e.getMessage());
111            LOG.error(IO_ERROR, e);
112        }
113        if (errorMessages.isEmpty()) {
114            try {
115                validateComponents(spec);
116            } catch (ComponentRegistryException e) {
117                errorMessages.add(COMPONENT_REGISTRY_EXCEPTION_ERROR + e);
118            }
119        }
120        return errorMessages.isEmpty();
121    }
122   
123    private byte[] getBytesFromInputStream() throws IOException {
124        int len;
125        byte[] b = new byte[4096];
126        final ByteArrayOutputStream bOS = new ByteArrayOutputStream();
127       
128        while ((len = input.read(b)) > 0) {
129            bOS.write(b, 0, len);
130        }
131       
132        return bOS.toByteArray();
133    }
134
135    private void validateComponents(CMDComponentSpec componentSpec) throws ComponentRegistryException {
136        validateComponents(Collections.singletonList(componentSpec.getCMDComponent()));
137    }
138   
139    private void validateComponents(List<CMDComponentType> cmdComponents) throws ComponentRegistryException {
140        for (CMDComponentType cmdComponentType : cmdComponents) {
141            validateDescribedComponents(cmdComponentType);
142            validateComponents(cmdComponentType.getCMDComponent());//Recursion
143        }
144    }
145   
146    private void validateDescribedComponents(CMDComponentType cmdComponentType) throws ComponentRegistryException {
147        checkPublicComponents(cmdComponentType);
148    }
149   
150    private void checkPublicComponents(CMDComponentType cmdComponentType) throws ComponentRegistryException {
151        if (isDefinedInSeparateFile(cmdComponentType)) {
152            String id = cmdComponentType.getComponentId();
153            CMDComponentSpec registeredComponent;
154            if (registry.isPublic()) { // public registry requires only published components
155                registeredComponent = registry.getMDComponent(id);
156                if (registeredComponent == null) {
157                    String error = cmdComponentType.getComponentId();
158                    if (userRegistry != null) {
159                        ComponentDescription desc = userRegistry.getComponentDescription(id);
160                        if (desc != null) {
161                            error = desc.getName() + " (" + cmdComponentType.getComponentId() + ")";
162                        }
163                    }
164                    errorMessages.add(COMPONENT_NOT_PUBLICLY_REGISTERED_ERROR + error);
165                }
166            } else { //User registry, can link to components from public registry and the user's registry
167                registeredComponent = registry.getMDComponent(id);
168                if (registeredComponent == null) {
169                    registeredComponent = publicRegistry.getMDComponent(id);
170                    if (registeredComponent == null) {
171                        errorMessages.add(COMPONENT_NOT_REGISTERED_ERROR + cmdComponentType.getComponentId());
172                    }
173                }
174               
175            }
176        }
177    }
178   
179    private boolean isDefinedInSeparateFile(CMDComponentType cmdComponentType) {
180        return cmdComponentType.getName() == null;
181    }
182
183    /**
184     * Do not call before having called {@link #validate() }!
185     *
186     * @return the spec unmarshalled during {@link #validate() }. If this has
187     * not been called, returns null.
188     */
189    public CMDComponentSpec getCMDComponentSpec() {
190        return spec;
191    }
192
193    /**
194     * Creates a fresh (re-unmarshalled) copy of the specification this instance
195     * has validated. If you are not going to alter this copy, you can re-use
196     * and share the copy used during validation by getting it from {@link #getCMDComponentSpec()
197     * }.
198     * <em>Do not call before having called {@link #validate() }!</em>
199     *
200     * @return a freshly unmarshalled copy of the spec based on the bytes
201     * collected from the input stream passed to {@link #validate() }. If this
202     * has not been called, returns null.
203     * @throws JAXBException exception occurred while marshalling from the input
204     * bytes
205     * @see #validate()
206     * @see #getCMDComponentSpec()
207     */
208    public CMDComponentSpec getCopyOfCMDComponentSpec() throws JAXBException {
209        // Re-unmarshall original bytes
210        return unmarshalSpec(originalSpecBytes);
211    }
212   
213    private CMDComponentSpec unmarshalSpec(byte[] inputBytes) throws JAXBException {
214        return marshaller.unmarshal(CMDComponentSpec.class, new ByteArrayInputStream(inputBytes), null);
215    }
216}
Note: See TracBrowser for help on using the repository browser.