1 /*****************************************************************************
5 * Creation date 06.10.2012
8 *****************************************************************************/
10 package org.splat.service;
13 import java.io.FileNotFoundException;
14 import java.io.IOException;
15 import java.text.ParseException;
16 import java.util.ArrayList;
17 import java.util.Date;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.List;
22 import org.apache.log4j.Logger;
23 import org.splat.dal.bo.kernel.User;
24 import org.splat.dal.bo.som.ConvertsRelation;
25 import org.splat.dal.bo.som.Document;
26 import org.splat.dal.bo.som.DocumentType;
27 import org.splat.dal.bo.som.ProgressState;
28 import org.splat.dal.bo.som.ProjectElement;
29 import org.splat.dal.bo.som.Publication;
30 import org.splat.dal.bo.som.SimulationContext;
31 import org.splat.dal.bo.som.SimulationContextType;
32 import org.splat.dal.bo.som.Study;
33 import org.splat.dal.bo.som.Timestamp;
34 import org.splat.dal.bo.som.UsedByRelation;
35 import org.splat.dal.bo.som.ValidationCycle;
36 import org.splat.dal.bo.som.ValidationStep;
37 import org.splat.dal.dao.som.ProjectElementDAO;
38 import org.splat.dal.dao.som.PublicationDAO;
39 import org.splat.dal.dao.som.TimestampDAO;
40 import org.splat.kernel.InvalidPropertyException;
41 import org.splat.kernel.MismatchException;
42 import org.splat.kernel.MissedPropertyException;
43 import org.splat.kernel.MultiplyDefinedException;
44 import org.splat.kernel.NotApplicableException;
45 import org.splat.manox.Reader;
46 import org.splat.manox.Toolbox;
47 import org.splat.service.technical.RepositoryService;
48 import org.splat.som.DocumentRights;
49 import org.splat.som.Revision;
50 import org.splat.som.Step;
51 import org.springframework.transaction.annotation.Transactional;
54 * Publication service implementation.
56 * @author <a href="mailto:roman.kozlov@opencascade.com">Roman Kozlov (RKV)</a>
58 public class PublicationServiceImpl implements PublicationService {
61 * Logger for this class.
63 protected final static Logger LOG = Logger
64 .getLogger(PublicationServiceImpl.class);
67 * Injected study service.
69 private StudyService _studyService;
71 * Injected step service.
73 private StepService _stepService;
75 * Injected document service.
77 private DocumentService _documentService;
79 * Injected document type service.
81 private DocumentTypeService _documentTypeService;
83 * Injected user service.
85 private UserService _userService;
87 * Injected project element service.
89 private ProjectElementService _projectElementService;
91 * Injected simulation context service.
93 private SimulationContextService _simulationContextService;
95 * Injected publication DAO.
97 private PublicationDAO _publicationDAO;
99 * Injected timestamp DAO.
101 private TimestampDAO _timestampDAO;
103 * Injected project element DAO.
105 private ProjectElementDAO _projectElementDAO;
107 * Injected repository service.
109 private RepositoryService _repositoryService;
114 * @see org.splat.service.PublicationService#copy(org.splat.dal.bo.som.Publication, org.splat.dal.bo.som.ProjectElement)
116 public Publication copy(final Publication aPublication,
117 final ProjectElement publisher) {
118 Publication copy = new Publication();
119 copy.setValue(aPublication.value());
120 copy.setStep(aPublication.getStep()); // May not be initialized yet
121 copy.setOwner(publisher);
122 copy.setIsnew(aPublication.getIsnew());
123 if (!copy.getOwnerStudy().equals(aPublication.getOwnerStudy())) {
124 copy.setIsnew('N'); // The referenced document is not new for the given study
132 * @see org.splat.service.PublicationService#createDoc(long, org.splat.som.Step, long, long, java.lang.String, java.lang.String,
133 * org.splat.dal.bo.som.ProgressState, java.lang.String, java.lang.String, java.util.Date, java.util.List)
136 public Publication createDoc(final long ownerId, final Step step,
137 final long documentTypeId, final long userId, final String fname,
138 final String doctitle, final ProgressState docstate,
139 final String reference, final String version, final Date docDate,
140 final List<Long> docuses) throws MissedPropertyException,
141 InvalidPropertyException, MultiplyDefinedException, IOException,
142 NotApplicableException, InterruptedException, ParseException {
143 DocumentType type = getDocumentTypeService().selectType(
144 (int) documentTypeId);
145 User user = getUserService().selectUser(userId);
146 File updir = getRepositoryService().getDownloadDirectory(user);
147 File upfile = new File(updir.getPath() + "/" + fname);
148 String[] table = fname.split("\\x2E");
150 // Creation of the document
151 Document.Properties dprop = new Document.Properties();
154 if (reference.length() == 0) { // Importation of a foreign document
155 // TODO: Extract property of supported documents (DOCX, ODT...)
156 addoc = getStepService().createDocument(
158 dprop.setName(doctitle).setType(type).setFormat(
159 table[table.length - 1]).setAuthor(user));
160 updir = addoc.getSourceFile().asFile();
161 if (LOG.isInfoEnabled()) {
162 LOG.info("Moving \"" + upfile.getAbsolutePath() + "\" to \""
163 + updir.getPath() + "\".");
165 upfile.renameTo(updir);
167 saveAs(addoc, docstate); // May throw FileNotFound if rename was not done
168 } catch (FileNotFoundException saverror) {
170 LOG.info("Waiting for the file.");
171 upfile.renameTo(updir);
172 saveAs(addoc, docstate); // Forget it if throw again FileNotFound
174 } else { // Importation of a previously created template-based document
175 if (docDate != null) {
176 dprop.setDate(docDate);
178 addoc = getStepService().assignDocument(step,
179 dprop.setReference(reference).setName(doctitle));
180 updir = addoc.getSourceFile().asFile();
181 if (LOG.isInfoEnabled()) {
182 LOG.info("Moving \"" + upfile.getName() + "\" to \""
183 + updir.getPath() + "\".");
185 upfile.renameTo(updir);
187 if (version.length() > 0) {
188 saveAs(addoc, new Revision(version));
190 saveAs(addoc, docstate);
192 } catch (FileNotFoundException saverror) {
194 LOG.info("Waiting for the file.");
195 upfile.renameTo(updir);
196 if (version.length() > 0) {
197 saveAs(addoc, new Revision(version));
199 saveAs(addoc, docstate);
203 // Creation of uses relations
204 if (docuses != null) {
205 for (Long index : docuses) {
206 Document used = getDocumentService().selectDocument(index);
207 addoc.addDependency(used);
216 * @see org.splat.service.PublicationService#versionDocument(org.splat.som.Step, org.splat.dal.bo.kernel.User, java.lang.String, long,
217 * java.lang.String, java.lang.String, org.splat.dal.bo.som.ProgressState, java.util.Date, java.lang.String[], long[])
220 public void versionDocument(final Step step, final User user,
221 final String filename, final long docIndex, final String docver,
222 final String summary, final ProgressState state, final Date date,
223 final String[] docuses, final long[] docusedby)
224 throws MissedPropertyException, InvalidPropertyException,
225 MultiplyDefinedException, IOException, MismatchException,
226 NotApplicableException, InterruptedException {
227 File updir = getRepositoryService().getDownloadDirectory(user);
228 File upfile = new File(updir.getPath() + "/" + filename);
230 // Versioning of the document
231 Document.Properties dprop = new Document.Properties();
232 Publication current = step.getDocument(docIndex);
235 if ((docver.length() != 0) && // Importation of a not foreign document
239 if ((summary != null) && (summary.length() > 0)) {
240 dprop.setDescription(summary);
242 next = getStepService().versionDocument(step, current,
243 dprop.setAuthor(user));
244 updir = next.getSourceFile().asFile();
245 if (LOG.isInfoEnabled()) {
246 LOG.info("Moving \"" + upfile.getName() + "\" to \""
247 + updir.getPath() + "\".");
249 upfile.renameTo(updir);
252 if (docver.length() == 0) { // Importation of a foreign document
253 saveAs(next, state); // May throw FileNotFound if rename was not done
255 saveAs(next, new Revision(docver));
257 } catch (FileNotFoundException saverror) {
259 LOG.info("Waiting for the file.");
260 upfile.renameTo(updir);
263 // TODO: Remove current document details from the contents of open study
265 // Creation of uses relations
266 updateRelations(current, next, docuses, docusedby);
270 * Update relations after creation of a new document version.
273 * the current version
277 * ids of used documents
279 * ids of documents used by the versioned one.
281 private void updateRelations(final Publication current,
282 final Publication next, final String[] docuses,
283 final long[] docusedby) {
284 if (docuses != null) {
285 for (int i = 0; i < docuses.length; i++) {
286 Long index = Long.valueOf(docuses[i].trim());
287 Document used = getDocumentService().selectDocument(index);// RKV: getPublication(index, steps);
288 next.addDependency(used);
291 // Outdating impacted document
292 HashSet<Long> compatible = new HashSet<Long>();
293 if (docusedby != null) {
294 for (int i = 0; i < docusedby.length; i++) {
295 compatible.add(docusedby[i]);
298 List<Publication> relist = current.getRelations(UsedByRelation.class);
299 for (Iterator<Publication> i = relist.iterator(); i.hasNext();) {
300 Publication using = i.next();
301 if (!compatible.contains(using.getIndex())) {
308 * protected Publication getPublication(int index, List<Step> steps) { for (Iterator<Step> i = steps.iterator(); i.hasNext();) { List<Publication>
309 * published = i.next().getAllDocuments(); for (Iterator<Publication> j = published.iterator(); j.hasNext();) { Publication found =
310 * j.next(); // In a given study step, if (found.value().getIndex() == index) return found; // there is only one publication of a given
311 * document } } return null; }
316 * @see org.splat.service.PublicationService#approve(org.splat.dal.bo.som.Publication, java.util.Date)
319 public Timestamp approve(final Publication aPublication, final Date adate) {
320 Timestamp res = null;
321 if (!(aPublication.isOutdated() || (aPublication.value()
322 .getProgressState() != ProgressState.inCHECK))) {
323 DocumentType type = aPublication.value().getType();
324 Study owner = aPublication.getOwnerStudy();
325 ValidationCycle cycle = getStudyService().getValidationCycleOf(
327 User approver = cycle.getActor(ValidationStep.APPROVAL);
328 Timestamp stamp = new Timestamp(ValidationStep.APPROVAL,
329 aPublication.value(), approver, adate);
330 getTimestampDAO().create(stamp);
332 if (getDocumentService().promote(aPublication.value(), stamp)) {
334 if (getDocumentService().isStudyResult(type)
335 && owner.getProgressState() == ProgressState.inCHECK) {
336 getStudyService().promote(owner);
340 return res; // Hoping that promotion of the study succeeded
346 * @see org.splat.service.PublicationService#demote(org.splat.dal.bo.som.Publication)
349 public boolean demote(final Publication aPublication) {
351 DocumentType type = aPublication.value().getType();
352 Study owner = aPublication.getOwnerStudy();
354 if (aPublication.value().getProgressState() == ProgressState.inCHECK) {
355 ValidationCycle cycle = getStudyService().getValidationCycleOf(
357 if (cycle.enables(ValidationStep.REVIEW)) {
358 res = getDocumentService().demote(aPublication.value());
360 res = getDocumentService().demote(aPublication.value());
362 getDocumentService().demote(aPublication.value());
365 } else if (aPublication.value().getProgressState() == ProgressState.inDRAFT) {
366 res = getDocumentService().demote(aPublication.value());
368 if (res && getDocumentService().isStudyResult(type)
369 && owner.getProgressState() != ProgressState.inWORK) {
370 getStudyService().demote(owner);
378 * @see org.splat.service.PublicationService#invalidate(org.splat.dal.bo.som.Publication)
381 public boolean invalidate(final Publication aPublication) {
383 if ((aPublication.value().getProgressState() == ProgressState.inCHECK)
384 && getDocumentService().demote(aPublication.value())) {
385 DocumentType type = aPublication.value().getType();
386 Study owner = aPublication.getOwnerStudy();
387 if (getDocumentService().isStudyResult(type)
388 && owner.getProgressState() == ProgressState.inCHECK) {
389 getStudyService().demote(owner);
399 * @see org.splat.service.PublicationService#promote(org.splat.dal.bo.som.Publication, java.util.Date)
402 public Timestamp promote(final Publication aPublication, final Date pdate) {
403 Timestamp res = null;
404 if ((!aPublication.isOutdated())
405 && (aPublication.value().getProgressState() == ProgressState.inWORK)) {
406 DocumentType type = aPublication.value().getType();
407 Study owner = aPublication.getOwnerStudy();
408 ValidationCycle cycle = getStudyService().getValidationCycleOf(
410 User promoter = cycle.getActor(ValidationStep.PROMOTION);
411 if (promoter == null) {
412 promoter = getInvolvedStep(aPublication).getActor();
414 if (promoter == null) {
415 promoter = owner.getAuthor();
417 Timestamp stamp = new Timestamp(ValidationStep.PROMOTION,
418 aPublication.value(), promoter, pdate);
419 getTimestampDAO().create(stamp);
421 if (getDocumentService().promote(aPublication.value(), stamp)) {
423 if (!cycle.enables(ValidationStep.REVIEW)) {
424 getDocumentService().promote(aPublication.value(), null);
426 if (getDocumentService().isStudyResult(type)
427 && owner.getProgressState() == ProgressState.inWORK) {
428 getStudyService().promote(owner);
432 return res; // Hoping that promotion of the study succeeded
438 * @see org.splat.service.PublicationService#review(org.splat.dal.bo.som.Publication, java.util.Date)
441 public Timestamp review(final Publication aPublication, final Date rdate) {
442 Timestamp res = null;
443 if (!aPublication.isOutdated()
444 && !(aPublication.value().getProgressState() != ProgressState.inDRAFT)) {
446 DocumentType type = aPublication.value().getType();
447 Study owner = aPublication.getOwnerStudy();
448 ValidationCycle cycle = getStudyService().getValidationCycleOf(
450 User reviewer = cycle.getActor(ValidationStep.REVIEW);
451 Timestamp stamp = new Timestamp(ValidationStep.REVIEW, aPublication
452 .value(), reviewer, rdate);
453 getTimestampDAO().create(stamp);
455 if (getDocumentService().promote(aPublication.value(), stamp)) {
457 if (getDocumentService().isStudyResult(type)
458 && owner.getProgressState() == ProgressState.inDRAFT) {
459 getStudyService().promote(owner);
463 return res; // Hoping that promotion of the study succeeded
469 * @see org.splat.service.PublicationService#saveAs(org.splat.dal.bo.som.Publication, org.splat.som.Revision)
474 public void saveAs(final Publication aPublication, final Revision newvers)
475 throws FileNotFoundException, NotApplicableException {
476 if (aPublication.value().isUndefined()) {
477 throw new NotApplicableException(
478 "Cannot save a Publication object refering an undefined Document");
480 if (!aPublication.value().getSourceFile().exists()) {
481 throw new FileNotFoundException();
484 getPublicationDAO().create(aPublication); // Must be done before updating the study in order to fix this final (rid-based) hascode
485 getDocumentService().updateAs(aPublication.value(), newvers); // May change the branch name of given revision
486 updateOwner(aPublication);
492 * @see org.splat.service.PublicationService#saveAs(org.splat.dal.bo.som.Publication, org.splat.dal.bo.som.ProgressState)
495 public void saveAs(final Publication aPublication, final ProgressState state)
496 throws FileNotFoundException, NotApplicableException {
497 if (aPublication.value().isUndefined()) {
498 throw new NotApplicableException(
499 "Cannot save a Publication object refering an undefined Document");
501 if (!aPublication.value().getSourceFile().exists()) {
502 throw new FileNotFoundException(aPublication.value().getSourceFile().asFile().getAbsolutePath());
505 if (state == ProgressState.inWORK || state == ProgressState.EXTERN) {
506 getPublicationDAO().create(aPublication); // Must be done before updating the study in order to fix this final (rid-based)
508 getDocumentService().updateAs(aPublication.value(), state);
510 DocumentType mytype = aPublication.value().getType();
511 Study owner = aPublication.getOwnerStudy();
512 ValidationCycle cycle = getStudyService().getValidationCycleOf(
514 boolean review = cycle.enables(ValidationStep.REVIEW);
515 if (!(state == ProgressState.inDRAFT && review)
516 && !(state == ProgressState.inCHECK && !review)) {
517 throw new NotApplicableException(
518 "Cannot save a result document in " + state.toString()
521 getPublicationDAO().create(aPublication); // Must be done before updating the study in order to fix this final (rid-based)
523 getDocumentService().updateAs(aPublication.value(),
524 ProgressState.inWORK);
526 promote(aPublication, aPublication.value()
527 .getLastModificationDate()); // Promotes to the appropriate state in accordance to the validation cycle
529 updateOwner(aPublication);
533 * Update an owner of the publication.
535 * @param aPublication
536 * the document publication
539 private void updateOwner(final Publication aPublication) {
540 Step step = getInvolvedStep(aPublication);
542 // Update of involved step
543 Document previous = aPublication.value().getPreviousVersion();
544 if (previous != null) {
545 Publication oldoc = step.getDocument(previous.getIndex());
546 getStepService().remove(step, oldoc); // Decrements the configuration tag count of document
548 getStepService().add(step, aPublication); // Increments the configuration tag count of document
550 // Import the document properties and update of the study
551 forwardProperties(aPublication, aPublication.value().getSourceFile()
553 getProjectElementDAO().merge(aPublication.getOwner());
557 * Propagate simulation contexts from the given config file to the publication's owner (study or step).
559 * @param aPublication
560 * the document publication
566 private void forwardProperties(final Publication aPublication,
567 final java.io.File from, final Step to) {
568 Reader tool = Toolbox.getReader(from);
569 if (tool != null) { // Properties extractor available for this type of document
570 SimulationContextType.Properties sprop = new SimulationContextType.Properties()
571 .setStep(to.getStep()).setProgressState(
572 ProgressState.APPROVED);
573 List<SimulationContextType> contype = getSimulationContextService()
574 .selectTypesWhere(sprop);
575 if (!contype.isEmpty()) { // There is an approved property type configured at this step
577 SimulationContext.Properties cprop = new SimulationContext.Properties();
578 List<SimulationContext> context = to.getAllSimulationContexts();
580 context = new ArrayList<SimulationContext>(context.size());
581 context.addAll(to.getAllSimulationContexts());
582 cprop.disableCheck();
583 for (Iterator<SimulationContextType> i = contype.iterator(); i
585 SimulationContextType property = i.next();
586 boolean isFound = false;
587 for (Iterator<SimulationContext> j = context.iterator(); j
589 SimulationContext existing = j.next();
590 isFound = existing.getType().equals(property);
592 // Forget this property as it is already set
598 String value = tool.extractProperty(property
601 continue; // Property not defined into the document
604 cprop.setType(property).setValue(value);
605 if (aPublication.getOwner() instanceof Study) {
606 getStudyService().addProjectContext(
607 (Study) aPublication.getOwner(), cprop); // Re-indexes knowledges and the study
610 .addSimulationContext(to, cprop); // Re-indexes knowledges only
612 } catch (Exception e) {
622 * Returns the study Step into which the document version referenced by this publication has been published.
624 * @param aPublication
625 * the document publication
626 * @return the study step where the document is published
628 public Step getInvolvedStep(final Publication aPublication) {
629 if (aPublication.getStep() == null) {
630 Step[] step = getProjectElementService().getSteps(
631 aPublication.getOwner());
632 for (int i = 0; i < step.length; i++) {
633 aPublication.setStep(step[i]); // The involved step necessarily exists
634 if (aPublication.value().isInto(aPublication.getStep())) {
639 return aPublication.getStep();
643 * Undo the out-date operation.
645 * @param aPublication
647 * @return true if the acceptance succeeds
649 * @see DocumentRights#canAccept()
652 public boolean actualize(final Publication aPublication) {
653 boolean res = aPublication.isOutdated();
655 aPublication.setIsnew('Y');
656 getPublicationDAO().update(aPublication);
662 * Out-dates this publication and recursively all publications using this one. Typically, a publication is out-dated when modifying a
663 * document to which it depends.
665 * @param aPublication
668 * @see #getProgressState()
671 public void outdate(final Publication aPublication) {
672 if (aPublication.isOutdated()) {
676 List<Publication> relist = aPublication
677 .getRelations(UsedByRelation.class);
678 for (Iterator<Publication> i = relist.iterator(); i.hasNext();) {
681 aPublication.setIsnew('O');
682 getPublicationDAO().update(aPublication);
686 * Create "Converts" relation for the given document publication and format.
688 * @param aPublication
689 * the document publication
692 * @return the created "Converts" relation
694 public ConvertsRelation attach(final Publication aPublication,
695 final String format) {
696 return getDocumentService().attach(aPublication.value(), format);
700 * Create "Converts" relation for the given document publication, format and description.
702 * @param aPublication
703 * the document publication
707 * the description of the relation
708 * @return the created "Converts" relation
710 public ConvertsRelation attach(final Publication aPublication,
711 final String format, final String description) {
712 return getDocumentService().attach(aPublication.value(), format,
717 * Rename the published document.
719 * @param aPublication
720 * the publication of the document
722 * the new document title
723 * @throws InvalidPropertyException
724 * if the new title is empty
726 public void rename(final Publication aPublication, final String title)
727 throws InvalidPropertyException {
728 getDocumentService().rename(aPublication.value(), title);
732 * Get the projectElementService.
734 * @return the projectElementService
736 public ProjectElementService getProjectElementService() {
737 return _projectElementService;
741 * Set the projectElementService.
743 * @param projectElementService
744 * the projectElementService to set
746 public void setProjectElementService(
747 final ProjectElementService projectElementService) {
748 _projectElementService = projectElementService;
752 * Get the simulationContextService.
754 * @return the simulationContextService
756 public SimulationContextService getSimulationContextService() {
757 return _simulationContextService;
761 * Set the simulationContextService.
763 * @param simulationContextService
764 * the simulationContextService to set
766 public void setSimulationContextService(
767 final SimulationContextService simulationContextService) {
768 _simulationContextService = simulationContextService;
772 * Get the studyService.
774 * @return the studyService
776 public StudyService getStudyService() {
777 return _studyService;
781 * Set the studyService.
783 * @param studyService
784 * the studyService to set
786 public void setStudyService(final StudyService studyService) {
787 _studyService = studyService;
791 * Get the stepService.
793 * @return the stepService
795 public StepService getStepService() {
800 * Set the stepService.
803 * the stepService to set
805 public void setStepService(final StepService stepService) {
806 _stepService = stepService;
810 * Get the documentService.
812 * @return the documentService
814 public DocumentService getDocumentService() {
815 return _documentService;
819 * Set the documentService.
821 * @param documentService
822 * the documentService to set
824 public void setDocumentService(final DocumentService documentService) {
825 _documentService = documentService;
829 * Get the publicationDAO.
831 * @return the publicationDAO
833 public PublicationDAO getPublicationDAO() {
834 return _publicationDAO;
838 * Set the publicationDAO.
840 * @param publicationDAO
841 * the publicationDAO to set
843 public void setPublicationDAO(final PublicationDAO publicationDAO) {
844 _publicationDAO = publicationDAO;
848 * Get the projectElementDAO.
850 * @return the projectElementDAO
852 public ProjectElementDAO getProjectElementDAO() {
853 return _projectElementDAO;
857 * Set the projectElementDAO.
859 * @param projectElementDAO
860 * the projectElementDAO to set
862 public void setProjectElementDAO(final ProjectElementDAO projectElementDAO) {
863 _projectElementDAO = projectElementDAO;
867 * Get the repositoryService.
869 * @return the repositoryService
871 public RepositoryService getRepositoryService() {
872 return _repositoryService;
876 * Set the repositoryService.
878 * @param repositoryService
879 * the repositoryService to set
881 public void setRepositoryService(final RepositoryService repositoryService) {
882 _repositoryService = repositoryService;
886 * Get the timestampDAO.
888 * @return the timestampDAO
890 public TimestampDAO getTimestampDAO() {
891 return _timestampDAO;
895 * Set the timestampDAO.
897 * @param timestampDAO
898 * the timestampDAO to set
900 public void setTimestampDAO(final TimestampDAO timestampDAO) {
901 _timestampDAO = timestampDAO;
905 * Get the documentTypeService.
907 * @return the documentTypeService
909 public DocumentTypeService getDocumentTypeService() {
910 return _documentTypeService;
914 * Set the documentTypeService.
916 * @param documentTypeService
917 * the documentTypeService to set
919 public void setDocumentTypeService(
920 final DocumentTypeService documentTypeService) {
921 _documentTypeService = documentTypeService;
925 * Get the userService.
927 * @return the userService
929 public UserService getUserService() {
934 * Set the userService.
937 * the userService to set
939 public void setUserService(final UserService userService) {
940 _userService = userService;