001/*
002 $Id: ItemFactory.java 3677 2010-04-20 12:02:33Z gregory $
003
004 Copyright (C) 2007 Fredrik Levander, Gregory Vincic, Olle Mansson
005
006 Files are copyright by their respective authors. The contributions to
007 files where copyright is not explicitly stated can be traced with the
008 source code revision system.
009
010 This file is part of Proteios.
011 Available at http://www.proteios.org/
012
013 proteios-2.x is free software; you can redistribute it and/or
014 modify it under the terms of the GNU General Public License
015 as published by the Free Software Foundation; either version 2
016 of the License, or (at your option) any later version.
017
018 Proteios is distributed in the hope that it will be useful,
019 but WITHOUT ANY WARRANTY; without even the implied warranty of
020 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
021 GNU General Public License for more details.
022
023 You should have received a copy of the GNU General Public License
024 along with this program; if not, write to the Free Software
025 Foundation, Inc., 59 Temple Place - Suite 330,
026 Boston, MA  02111-1307, USA.
027 */
028package org.proteios.core;
029
030import org.apache.log4j.LogManager;
031import org.apache.log4j.Logger;
032import org.proteios.core.Job.Status;
033import org.proteios.core.data.BasicData;
034import org.proteios.core.data.CreationInterface;
035import org.proteios.core.data.DirectoryData;
036import org.proteios.core.data.FileData;
037import org.proteios.core.data.JobData;
038import org.proteios.core.data.QuotaData;
039import org.proteios.core.data.UserData;
040import org.proteios.core.plugin.Plugin;
041
042import java.util.Date;
043
044/**
045 * Used to create persistent item instances
046 * 
047 * @author gregory
048 */
049public class ItemFactory
050{
051        private static final Logger log = LogManager.getLogger(ItemFactory.class
052                .getName());
053        private DbControl dc = null;
054
055
056        /**
057         * Remember to set the DbControl before using the factory
058         */
059        public ItemFactory()
060        {}
061
062
063        /**
064         * @param dc initial DbControl to use
065         */
066        public ItemFactory(DbControl dc)
067        {
068                this.dc = dc;
069        }
070
071
072        /**
073         * @return currently use DbControled or null if none is set
074         */
075        public DbControl getDc()
076        {
077                return dc;
078        }
079
080
081        /**
082         * We allow outside change of DbControl in use to enable factory reuse
083         * 
084         * @param dc
085         */
086        public void setDc(DbControl dc)
087        {
088                this.dc = dc;
089        }
090
091
092        /**
093         * Creates an instance of the given template class. Default values are set
094         * wherever possible. The returned objects are not saved and you should make
095         * sure to call dc.saveItem(item) for each item created by this method.
096         * 
097         * @param <D>
098         * @param template item class to create
099         * @param extraParameters optional Object[] array of extra arguments
100         * @return object of type D or null if item couldn't be created
101         */
102        public <D extends BasicItem<?>> D create(Class<D> template,
103                        Object... extraParameters)
104        {
105                if (dc != null && !dc.isClosed())
106                {
107                        D item = null;
108                        User owner = null;
109                        if (extraParameters != null && extraParameters.length > 0)
110                        {
111                                // Invoke constructor taking extra arguments
112                                item = dc.newItem(template, extraParameters);
113                        }
114                        else
115                        {
116                                item = dc.newItem(template);
117                        }
118                        BasicData data = item.getData();
119                        // Set default values. Interfaces or AbstractClasses should be
120                        // checked here.
121                        // Specific item classes should not be handled here. Those settings
122                        // should be done outside of the ItemFactory.
123                        if (item instanceof Nameable)
124                                ((Nameable) item).setName("New " + item.getClass()
125                                        .getSimpleName());
126                        if (item instanceof Ownable)
127                        {
128                                owner = getById(User.class, dc.getSessionControl()
129                                        .getLoggedInUserId());
130                                ((Ownable) item).setOwner(owner);
131                        }
132                        if (data instanceof CreationInterface)
133                                ((CreationInterface) data).setCreated(new Date());
134                        // New Extracts and Samples should also have a creation event
135                        /*
136                         * if (data instanceof MeasuredBioMaterialData) {
137                         * MeasuredBioMaterial mbm= (MeasuredBioMaterial) item;
138                         * createCreationEvent(mbm); }
139                         */
140                        log.debug("Creating " + item);
141                        return item;
142                }
143                log.error("DbControl is not set or is null in ItemFactory");
144                return null;
145        }
146
147
148        /***************************************************************************
149         * Methods for creating specific items with default values not specified
150         * within an interface or abstract class. Most of these methods should be
151         * removed and the initialization should be done outside.
152         **************************************************************************/
153        /**
154         * TODO this method does two things, create and configures a job to a
155         * starting state. Split this up and move the configure part into the Job
156         * class itself. Create a new <code>Job</code> item.
157         * 
158         * @param config The plugin configuration this job should use or null if the
159         *        job is executed by an external program
160         * @return The new <code>Job</code> item
161         * @throws BaseException If there is an error
162         */
163        public Job createJob(PluginDefinition plugin, PluginConfiguration config)
164                        throws BaseException
165        {
166                Job j = create(Job.class);
167                JobData jobData = j.getData();
168                if (config == null && plugin == null)
169                {
170                        jobData.setType(Job.Type.OTHER.getValue());
171                        jobData.setPriority(5);
172                }
173                else
174                {
175                        if (plugin == null)
176                                plugin = config.getPluginDefinition();
177                        if (plugin == null)
178                                throw new InvalidUseOfNullException("plugin");
179                        plugin.checkPermission(Permission.USE);
180                        jobData.setPluginDefinition(plugin.getData());
181                        if (config != null)
182                                config.checkPermission(Permission.USE);
183                        jobData.setPluginConfiguration(config == null ? null : config
184                                .getData());
185                        jobData.setType(Job.Type.RUN_PLUGIN.getValue());
186                        Plugin.MainType pluginType = plugin.getMainType();
187                        if (pluginType == Plugin.MainType.IMPORT)
188                        {
189                                jobData.setPriority(4);
190                        }
191                        else
192                        {
193                                jobData.setPriority(5);
194                        }
195                }
196                j.setName("New job");
197                jobData.setStatus(Status.UNCONFIGURED.getValue());
198                jobData.setCreated(new Date());
199                return j;
200        }
201
202
203        /**
204         * Create a new <code>User</code> item.
205         * 
206         * @param login The login for the user (required)
207         * @param password The password for the user (required)
208         * @return The new <code>User</code> item
209         * @throws BaseException If there is an error
210         */
211        public User createUser(String login, String password)
212                        throws BaseException
213        {
214                User u = create(User.class);
215                u.setLogin(login);
216                u.setPassword(password);
217                u.getData().setQuota(
218                        HibernateUtil.loadData(dc.getHibernateSession(), QuotaData.class,
219                                SystemItems.getId(Quota.DEFAULT)));
220                return u;
221        }
222
223
224        /**
225         * Create a new <code>BioMaterialEvent</code> of the Type#OTHER
226         * type. If only a single BioMaterial was used it can be given as parameter
227         * optionally with the used amount of the biomaterial
228         * 
229         * @param bioMaterial The affected biomaterial
230         * @return The new <code>BioMaterialEvent</code> item
231         * @throws BaseException If there is an error
232         */
233        public CreationEvent createCreationEvent(MeasuredBioMaterial<?> bioMaterial,
234                        MeasuredBioMaterial<?> sourceMaterial, Float usedQuantity)
235                        throws BaseException
236        {
237                CreationEvent bme = create(CreationEvent.class);
238                bme.setEntryDate(new Date());
239                bme.setEventDate(new Date());
240                org.hibernate.Session session = dc.getHibernateSession();
241                SessionControl sc = dc.getSessionControl();
242                bme.setUser(HibernateUtil.loadData(session, UserData.class, sc
243                        .getLoggedInUserId()));
244                bme.addCreatedMeasuredBioMaterial(bioMaterial);
245                if (sourceMaterial != null)
246                {
247                        bme.setSource(sourceMaterial, usedQuantity);
248                }
249                return bme;
250        }
251
252
253        /**
254         * Creates and saves a home directory for the supplied user
255         * 
256         * @param user
257         * @return created directory
258         * @throws PermissionDeniedException
259         * @throws BaseException
260         */
261        public Directory createHomeDirectory(User user)
262                        throws PermissionDeniedException, BaseException
263        {
264                SessionControl sc = dc.getSessionControl();
265                user.checkPermission(Permission.WRITE);
266                if (!sc.hasPermission(Permission.CREATE, Item.DIRECTORY))
267                {
268                        throw new PermissionDeniedException(Permission.CREATE, "Directory");
269                }
270                Directory allHome = getById(Directory.class, SystemItems
271                        .getId(Directory.HOME));
272                Directory userHome = create(Directory.class);
273                userHome.setParent(allHome);
274                userHome.setName(user.getLogin());
275                userHome.setOwner(user);
276                userHome.setProjectKey(null);
277                user.setHomeDirectory(userHome);
278                dc.saveItem(userHome);
279                user.setHomeDirectory(userHome);
280                return userHome;
281        }
282
283
284        /**
285         * Create a new <code>LabeledExtract</code> with label.
286         * 
287         * @param label The labeling compound used for labeling the extract
288         * @return The new <code>LabeledExtract</code> item
289         * @throws BaseException If there is an error
290         */
291        public LabeledExtract createLabeledExtract(Label label)
292                        throws BaseException
293        {
294                LabeledExtract le = create(LabeledExtract.class);
295                le.setLabel(label);
296                return le;
297        }
298
299
300        /**
301         * Create a new <code>AnnotationType</code> item.
302         * 
303         * @param valueType The type of values to use
304         * @return The new <code>AnnotationType</code> item
305         * @throws BaseException If there is an error
306         */
307        public AnnotationType createAnnotationType(org.proteios.core.Type valueType)
308                        throws BaseException
309        {
310                if (valueType == null)
311                        throw new InvalidUseOfNullException("valueType");
312                AnnotationType at = create(AnnotationType.class);
313                at
314                        .setName("New " + valueType.toString().toLowerCase() + " annotation type");
315                at.getData().setValueType(valueType.getValue());
316                at.setMultiplicity(1);
317                at.setRequiredForMiame(false);
318                at.setHeight(valueType == org.proteios.core.Type.TEXT ? 4 : 1);
319                at.setWidth(40);
320                return at;
321        }
322
323
324        /**
325         * Create a new <code>GelImageAnalysisEvent</code> of the Type#OTHER type.
326         * 
327         * @param bioMaterial The affected biomaterial
328         * @return The new <code>GelImageAnalysisEvent</code> item
329         * @throws BaseException If there is an error
330         */
331        public GelImageAnalysisEvent createGelImageAnalysisEvent(
332                        MeasuredBioMaterial<?> bioMaterial)
333                        throws BaseException
334        {
335                GelImageAnalysisEvent giae = create(GelImageAnalysisEvent.class);
336                giae.setBioMaterial(bioMaterial);
337                giae.setEntryDate(new Date());
338                giae.setEventDate(new Date());
339                org.hibernate.Session session = dc.getHibernateSession();
340                SessionControl sc = dc.getSessionControl();
341                giae.setUser(HibernateUtil.loadData(session, UserData.class, sc
342                        .getLoggedInUserId()));
343                return giae;
344        }
345
346
347        /**
348         * Create a new <code>GelScanEvent</code> of the Type#OTHER type.
349         * 
350         * @param bioMaterial The affected biomaterial
351         * @return The new <code>GelScanEvent</code> item
352         * @throws BaseException If there is an error
353         */
354        public GelScanEvent createGelScanEvent(MeasuredBioMaterial<?> bioMaterial)
355                        throws BaseException
356        {
357                GelScanEvent gse = create(GelScanEvent.class);
358                gse.setBioMaterial(bioMaterial);
359                gse.setEntryDate(new Date());
360                gse.setEventDate(new Date());
361                org.hibernate.Session session = dc.getHibernateSession();
362                SessionControl sc = dc.getSessionControl();
363                gse.setUser(HibernateUtil.loadData(session, UserData.class, sc
364                        .getLoggedInUserId()));
365                return gse;
366        }
367
368
369        /**
370         * Create a new <code>SeparationEvent</code> of the Type#OTHER type.
371         * 
372         * @param bioMaterial The affected biomaterial
373         * @return The new <code>SeparationEvent</code> item
374         * @throws BaseException If there is an error
375         */
376        public SeparationEvent createSeparationEvent(
377                        MeasuredBioMaterial<?> bioMaterial, Float usedQuantity)
378                        throws BaseException
379        {
380                SeparationEvent se = create(SeparationEvent.class);
381                se.setSource(bioMaterial, usedQuantity);
382                se.setEntryDate(new Date());
383                se.setEventDate(new Date());
384                org.hibernate.Session session = dc.getHibernateSession();
385                SessionControl sc = dc.getSessionControl();
386                se.setUser(HibernateUtil.loadData(session, UserData.class, sc
387                        .getLoggedInUserId()));
388                return se;
389        }
390
391
392        /**
393         * Create a new <code>StainingEvent</code> of the Type#OTHER type.
394         * 
395         * @param bioMaterial The affected biomaterial
396         * @return The new <code>StainingEvent</code> item
397         * @throws BaseException If there is an error
398         */
399        public StainingEvent createStainingEvent(MeasuredBioMaterial<?> bioMaterial)
400                        throws BaseException
401        {
402                StainingEvent se = create(StainingEvent.class);
403                se.setBioMaterial(bioMaterial);
404                se.setEntryDate(new Date());
405                se.setEventDate(new Date());
406                org.hibernate.Session session = dc.getHibernateSession();
407                SessionControl sc = dc.getSessionControl();
408                se.setUser(HibernateUtil.loadData(session, UserData.class, sc
409                        .getLoggedInUserId()));
410                return se;
411        }
412
413
414        /**
415         * Create a new <code>UpdateEvent</code> of the Type#OTHER type.
416         * 
417         * @param bioMaterial The affected biomaterial
418         * @return The new <code>UpdateEvent</code> item
419         * @throws BaseException If there is an error
420         */
421        public UpdateEvent createUpdateEvent(MeasuredBioMaterial<?> bioMaterial)
422                        throws BaseException
423        {
424                UpdateEvent ue = create(UpdateEvent.class);
425                ue.setBioMaterial(bioMaterial);
426                ue.setEntryDate(new Date());
427                ue.setEventDate(new Date());
428                org.hibernate.Session session = dc.getHibernateSession();
429                SessionControl sc = dc.getSessionControl();
430                ue.setUser(HibernateUtil.loadData(session, UserData.class, sc
431                        .getLoggedInUserId()));
432                return ue;
433        }
434
435
436        /**
437         * Create a new <code>SeparationMethod</code> item.
438         * 
439         * @param externalId The external id of the new item
440         * @return The new <code>SeparationMethod</code> item
441         * @throws BaseException If there is an error
442         */
443        public SeparationMethod<?> createSeparationMethod(String externalId)
444                        throws BaseException
445        {
446                SeparationMethod<?> sm = create(SeparationMethod.class);
447                sm.setExternalId(externalId);
448                return sm;
449        }
450
451
452        /**
453         * Create a new <code>GlobalDefaultSetting</code> item.
454         * 
455         * @param name The name of the setting
456         * @param value The value of the setting
457         * @return The new <code>ClientDefaultSetting</code> item
458         * @throws BaseException If there is an error
459         */
460        public GlobalDefaultSetting createGlobalDefaultSetting(String name,
461                        String value)
462                        throws BaseException
463        {
464                GlobalDefaultSetting gds = create(GlobalDefaultSetting.class);
465                gds.setName(name);
466                gds.setValue(value);
467                return gds;
468        }
469
470
471        /**
472         * Create a new <code>Message</code> item.
473         * 
474         * @param to the user the message shuold be sent to
475         * @param fromName the name of the sender of the message
476         * @param fromUser the user sending the message
477         * @param job the job this message is about
478         * @return return The new <code>Message</code> item
479         * @throws BaseException If there is an error
480         */
481        public Message createMessage(User to, String fromName, User fromUser,
482                        Job job)
483                        throws BaseException
484        {
485                Message m = create(Message.class, to, fromName, fromUser, job);
486                return m;
487        }
488
489
490        <T extends BasicItem<?>> T getItem(Class<T> name, BasicData data,
491                        Object... extraParameters)
492        {
493                log.debug("getItem [" + name + "] [" + data + "]");
494                return dc.getItem(name, data, extraParameters);
495        }
496
497
498        public <T extends BasicItem<?>> T getById(Class<T> name, Integer id)
499        {
500                log.debug("getById [" + name + "] [" + id + "]");
501                if (dc != null && !dc.isClosed())
502                {
503                        return dc.loadItem(name, id);
504                }
505                return null;
506        }
507
508
509        /**
510         * Get a <code>File</code> item when you know the path. Use the create
511         * parameter to specify if a new file should be create if it doesn't exists.
512         * The directory part of the path must still be an existing path, otherwise
513         * an <code>ItemNotFoundException</code> is thrown.
514         * 
515         * @param path The path to the file to load
516         * @param create TRUE if a new file should be created if one doesn't eixts,
517         *        FALSE to throw an exception (DbControl.saveItem must always be
518         *        called)
519         * @return The <code>File</code> item
520         * @throws ItemNotFoundException If an item with the specified path is not
521         *         found and create is FALSE
522         * @throws PermissionDeniedException If the logged in user doesn't have
523         *         {@link Permission#READ} permission to the item
524         * @throws BaseException If there is another error
525         */
526        public File getByPath(Path path, boolean create)
527                        throws ItemNotFoundException, PermissionDeniedException,
528                        BaseException
529        {
530                int directoryId = 0;
531                File file = null;
532                try
533                {
534                        directoryId = getIdFromPath(path);
535                }
536                catch (ItemNotFoundException ex)
537                {
538                        throw new ItemNotFoundException("File[path=" + path + "]");
539                }
540                org.hibernate.Query query = HibernateUtil.getPredefinedQuery(dc
541                        .getHibernateSession(), "GET_FILE_IN_DIRECTORY");
542                /*
543                 * SELECT f FROM File f WHERE f.directory = :directory AND f.name =
544                 * :name
545                 */
546                query.setInteger("directory", directoryId);
547                query.setString("name", path.getFilename());
548                FileData fileData = HibernateUtil.loadData(FileData.class, query);
549                if (fileData == null)
550                {
551                        if (!create)
552                        {
553                                throw new ItemNotFoundException("File[path=" + path + "]");
554                        }
555                        Directory d = getByPath(path);
556                        file = create(File.class);
557                        file.setDirectory(d);
558                        file.setName(path.getFilename());
559                }
560                else
561                {
562                        file = dc.getItem(File.class, fileData);
563                }
564                return file;
565        }
566
567
568        public <T extends BasicItem<?>> T getBySystemId(Class<T> name, String id)
569        {
570                log.debug("Loading [" + name + "] [" + id + "]");
571                if (dc != null && !dc.isClosed())
572                {
573                        return getById(name, SystemItems.getId(id));
574                }
575                return null;
576        }
577
578
579        /**
580         * Get the id of a directory when you know the path.
581         * 
582         * @param path The path to the directory to load
583         * @return The id of the <code>Directory</code> item
584         * @throws ItemNotFoundException If an item with the specified path is not
585         *         found
586         * @throws BaseException If there is another error
587         */
588        private int getIdFromPath(Path path)
589                        throws BaseException
590        {
591                DirectoryData current = null;
592                org.hibernate.Session session = dc.getHibernateSession();
593                if (path.getUserLogin() != null)
594                {
595                        org.hibernate.Query query = HibernateUtil.getPredefinedQuery(dc
596                                .getHibernateSession(), "GET_USER_FOR_LOGIN");
597                        query.setString("login", path.getUserLogin());
598                        UserData userData = HibernateUtil.loadData(UserData.class, query);
599                        if (userData == null)
600                        {
601                                throw new ItemNotFoundException("User[login=" + path
602                                        .getUserLogin() + "]");
603                        }
604                        current = userData.getHomeDirectory();
605                }
606                else
607                {
608                        current = HibernateUtil.loadData(session, DirectoryData.class,
609                                SystemItems.getId(Directory.ROOT));
610                }
611                if (current == null)
612                        throw new ItemNotFoundException("Directory[path=" + path + "]");
613                org.hibernate.Query query = HibernateUtil.getPredefinedQuery(session,
614                        "GET_SUBDIRECTORY_WITH_NAME");
615                for (int i = 0; i < path.getDirectoryCount(); i++)
616                {
617                        query.setEntity("parent", current);
618                        query.setString("name", path.getDirectory(i));
619                        current = HibernateUtil.loadData(DirectoryData.class, query);
620                        if (current == null)
621                        {
622                                throw new ItemNotFoundException("Directory[path=" + path + "]");
623                        }
624                }
625                return current.getId();
626        }
627
628
629        /**
630         * Get a <code>Directory</code> item when you know the path.
631         * 
632         * @param path The path to the directory to load
633         * @return The <code>Directory</code> item
634         * @throws ItemNotFoundException If an item with the specified path is not
635         *         found
636         * @throws PermissionDeniedException If the logged in user doesn't have
637         *         {@link Permission#READ} permission to the item
638         * @throws BaseException If there is another error
639         */
640        public Directory getByPath(Path path)
641                        throws ItemNotFoundException, PermissionDeniedException,
642                        BaseException
643        {
644                int directoryId = getIdFromPath(path);
645                return getById(Directory.class, directoryId);
646        }
647}