diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/FileBasedTrackingDAO.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/FileBasedTrackingDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..26652055ddc5792e30600e65ffc3eba732306efd --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/FileBasedTrackingDAO.java @@ -0,0 +1,97 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.main; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.IOUtils; + +import ch.ethz.bsse.cisd.dsu.tracking.utils.LogUtils; +import ch.systemsx.cisd.common.parser.AbstractParserObjectFactory; +import ch.systemsx.cisd.common.parser.IParserObjectFactory; +import ch.systemsx.cisd.common.parser.IParserObjectFactoryFactory; +import ch.systemsx.cisd.common.parser.IPropertyMapper; +import ch.systemsx.cisd.common.parser.ParserException; +import ch.systemsx.cisd.common.parser.TabFileLoader; + +/** + * @author Tomasz Pylak + */ +public class FileBasedTrackingDAO implements ITrackingDAO +{ + private final String filePath; + + public FileBasedTrackingDAO(String filePath) + { + this.filePath = filePath; + } + + public void saveTrackingState(TrackingStateDTO state) + { + List<String> lines = new ArrayList<String>(); + lines.add("lastSeenSequencingSampleId\tlastSeenFlowLaneSampleId\tlastSeenDatasetId\n"); + lines.add("" + state.getLastSeenSequencingSampleId() + "\t" + + state.getLastSeenFlowLaneSampleId() + "\t" + state.getLastSeenDatasetId()); + writeLines(new File(filePath), lines); + } + + public TrackingStateDTO getTrackingState() + { + TabFileLoader<TrackingStateDTO> tabFileLoader = + new TabFileLoader<TrackingStateDTO>( + new IParserObjectFactoryFactory<TrackingStateDTO>() + { + public IParserObjectFactory<TrackingStateDTO> createFactory( + IPropertyMapper propertyMapper) throws ParserException + { + return new AbstractParserObjectFactory<TrackingStateDTO>( + TrackingStateDTO.class, propertyMapper) + { + }; + } + }); + try + { + List<TrackingStateDTO> trackingState = tabFileLoader.load(new File(filePath)); + if (trackingState.size() != 1) + { + throw LogUtils.environmentError("File %s has to many rows, it should have exactly 1.", + filePath); + } + return trackingState.get(0); + } catch (Exception e) + { + throw LogUtils.envErr("Incorrect file format", e); + } + } + + private static void writeLines(File file, List<String> lines) + { + try + { + IOUtils.writeLines(lines, "\n", new FileOutputStream(file)); + } catch (IOException ex) + { + throw LogUtils.envErr(String.format("Cannot save the file %s with content: %s", + file.getPath(), lines), ex); + } + } +} diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/ITrackingDAO.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/ITrackingDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..0a3cf3bb9a38820584ba9a4c72732a91b15ab750 --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/ITrackingDAO.java @@ -0,0 +1,27 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.main; + +/** + * @author Tomasz Pylak + */ +public interface ITrackingDAO +{ + void saveTrackingState(TrackingStateDTO state); + + TrackingStateDTO getTrackingState(); +} diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/Parameters.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/Parameters.java new file mode 100644 index 0000000000000000000000000000000000000000..ee0bbaa64de16b0372c0aa71213b95d50d4da71d --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/Parameters.java @@ -0,0 +1,73 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.main; + +import static ch.systemsx.cisd.common.utilities.PropertyUtils.getMandatoryProperty; + +import java.util.Properties; + +import ch.systemsx.cisd.common.mail.IMailClient; +import ch.systemsx.cisd.common.mail.MailClient; +import ch.systemsx.cisd.common.utilities.PropertyUtils; + +/** + * @author Tomasz Pylak + */ +public class Parameters +{ + private static final String OPENBIS_USER = "openbis-user"; + + private static final String OPENBIS_PASSWORD = "openbis-password"; + + private static final String OPENBIS_SERVER_URL = "openbis-server-url"; + + private final String openbisUser; + + private final String openbisPassword; + + private final String openbisServerURL; + + private final IMailClient mailClient; + + public Parameters(Properties props) + { + this.openbisUser = getMandatoryProperty(props, OPENBIS_USER); + this.openbisPassword = getMandatoryProperty(props, OPENBIS_PASSWORD); + this.openbisServerURL = getMandatoryProperty(props, OPENBIS_SERVER_URL); + this.mailClient = new MailClient(props); + } + + public String getOpenbisUser() + { + return openbisUser; + } + + public String getOpenbisPassword() + { + return openbisPassword; + } + + public String getOpenbisServerURL() + { + return openbisServerURL; + } + + public IMailClient getMailClient() + { + return mailClient; + } +} diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingBO.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingBO.java new file mode 100644 index 0000000000000000000000000000000000000000..3b33902967d315680fae6a100927f0f55ad15bc8 --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingBO.java @@ -0,0 +1,124 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.main; + +import java.util.List; + +import ch.ethz.bsse.cisd.dsu.tracking.Email; +import ch.ethz.bsse.cisd.dsu.tracking.IEntityTrackingEmailGenerator; +import ch.ethz.bsse.cisd.dsu.tracking.TrackedEntities; +import ch.systemsx.cisd.common.mail.From; +import ch.systemsx.cisd.common.mail.IMailClient; +import ch.systemsx.cisd.openbis.generic.shared.ITrackingServer; +import ch.systemsx.cisd.openbis.generic.shared.basic.IIdentifiable; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TrackingDataSetCriteria; +import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TrackingSampleCriteria; +import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO; + +/** + * @author Tomasz Pylak + */ +public class TrackingBO +{ + private static final String SEQUENCING_SAMPLE_TYPE = "ILLUMINA_SEQUENCING"; + + private static final String FLOW_LANE_SAMPLE_TYPE = "FLOW_LANE"; + + private final ITrackingServer trackingServer; + + private final IEntityTrackingEmailGenerator emailGenerator; + + private final IMailClient mailClient; + + public TrackingBO(ITrackingServer trackingServer, IEntityTrackingEmailGenerator emailGenerator, + IMailClient mailClient) + { + this.trackingServer = trackingServer; + this.emailGenerator = emailGenerator; + this.mailClient = mailClient; + } + + public void trackAndNotify(ITrackingDAO trackingDAO, SessionContextDTO session) + { + TrackingStateDTO prevTrackingState = trackingDAO.getTrackingState(); + + TrackedEntities changedEntities = + fetchChangedEntities(prevTrackingState, trackingServer, session); + List<Email> emails = emailGenerator.generateEmails(changedEntities); + sendEmails(emails, mailClient); + saveTrackingState(changedEntities, trackingDAO); + } + + private static void sendEmails(List<Email> emails, IMailClient mailClient) + { + for (Email email : emails) + { + From from = (email.getFromOrNull() == null) ? null : new From(email.getFromOrNull()); + mailClient.sendMessage(email.getSubject(), email.getContent(), + email.getReplyToOrNull(), from, email.getRecipients()); + } + } + + private static void saveTrackingState(TrackedEntities changedEntities, ITrackingDAO trackingDAO) + { + TrackingStateDTO state = new TrackingStateDTO(); + state.setLastSeenSequencingSampleId(calcMaxId(changedEntities.getSequencingSamples())); + state.setLastSeenFlowLaneSampleId(calcMaxId(changedEntities.getFlowLaneSamples())); + state.setLastSeenDatasetId(calcMaxId(changedEntities.getDataSets())); + trackingDAO.saveTrackingState(state); + } + + private static int calcMaxId(List<? extends IIdentifiable> entities) + { + long max = 0; + for (IIdentifiable entity : entities) + { + max = Math.max(max, entity.getId()); + } + // FIXME 2009--, Tomasz Pylak: refactor ids to long everywhere + return (int) max; + } + + private static TrackedEntities fetchChangedEntities(TrackingStateDTO trackingState, + ITrackingServer trackingServer, SessionContextDTO session) + { + List<Sample> sequencingSamples = + listSamples(SEQUENCING_SAMPLE_TYPE, trackingState.getLastSeenSequencingSampleId(), + trackingServer, session); + + List<Sample> flowLaneSamples = + listSamples(FLOW_LANE_SAMPLE_TYPE, trackingState.getLastSeenFlowLaneSampleId(), + trackingServer, session); + + TrackingDataSetCriteria dataSetCriteria = + new TrackingDataSetCriteria(FLOW_LANE_SAMPLE_TYPE, trackingState + .getLastSeenDatasetId()); + List<ExternalData> dataSets = + trackingServer.listDataSets(session.getSessionToken(), dataSetCriteria); + + return new TrackedEntities(sequencingSamples, flowLaneSamples, dataSets); + } + + private static List<Sample> listSamples(String sampleType, int lastSeenSampleId, + ITrackingServer trackingServer, SessionContextDTO session) + { + TrackingSampleCriteria criteria = new TrackingSampleCriteria(sampleType, lastSeenSampleId); + return trackingServer.listSamples(session.getSessionToken(), criteria); + } +} diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingClient.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingClient.java new file mode 100644 index 0000000000000000000000000000000000000000..644dfc53fcf4e0b8061aa8720287115c83811d10 --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingClient.java @@ -0,0 +1,114 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.main; + +import java.util.Properties; + +import org.apache.log4j.Logger; + +import ch.ethz.bsse.cisd.dsu.tracking.EntityTrackingEmailGenerator; +import ch.ethz.bsse.cisd.dsu.tracking.IEntityTrackingEmailGenerator; +import ch.ethz.bsse.cisd.dsu.tracking.utils.LogUtils; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; +import ch.systemsx.cisd.common.logging.LogInitializer; +import ch.systemsx.cisd.common.mail.IMailClient; +import ch.systemsx.cisd.common.spring.HttpInvokerUtils; +import ch.systemsx.cisd.common.utilities.PropertyUtils; +import ch.systemsx.cisd.openbis.generic.shared.ITrackingServer; +import ch.systemsx.cisd.openbis.generic.shared.dto.SessionContextDTO; + +/** + * @author Tomasz Pylak + */ +public class TrackingClient +{ + private static final String SERVICE_PROPERTIES_FILE = "etc/tracking-service.properties"; + + private static final String LOCAL_STORAGE_FILE = "etc/tracking-local-database"; + + public static void main(String[] args) + { + try + { + track(); + } catch (EnvironmentFailureException ex) + { + LogUtils.notify(ex); + } catch (Throwable ex) + { + LogUtils.notify(ex); + } + } + + private static void track() + { + LogInitializer.init(); + Properties props = PropertyUtils.loadProperties(SERVICE_PROPERTIES_FILE); + Parameters params = new Parameters(props); + + ITrackingServer trackingServer = createOpenBISTrackingServer(params); + IEntityTrackingEmailGenerator emailGenerator = new EntityTrackingEmailGenerator(props); + IMailClient mailClient = params.getMailClient(); + TrackingBO trackingBO = new TrackingBO(trackingServer, emailGenerator, mailClient); + + ITrackingDAO trackingDAO = new FileBasedTrackingDAO(LOCAL_STORAGE_FILE); + + SessionContextDTO session = authentificateInOpenBIS(params, trackingServer); + + trackingBO.trackAndNotify(trackingDAO, session); + } + + private static ITrackingServer createOpenBISTrackingServer(Parameters params) + { + return HttpInvokerUtils.createServiceStub(ITrackingServer.class, params + .getOpenbisServerURL(), 5); + } + + private static SessionContextDTO authentificateInOpenBIS(Parameters params, + ITrackingServer trackingServer) + { + try + { + String openbisUser = params.getOpenbisUser(); + SessionContextDTO session = + trackingServer.tryToAuthenticate(openbisUser, params.getOpenbisPassword()); + if (session == null) + { + throw createAuthentificationException(params, null); + } + return session; + } catch (Exception ex) + { + throw createAuthentificationException(params, ex); + } + } + + private static EnvironmentFailureException createAuthentificationException(Parameters params, + Exception exOrNull) + { + String exceptionMsg = + (exOrNull == null) ? "" : " Unexpected exception has occured: " + + exOrNull.getMessage(); + return LogUtils + .environmentError( + "Cannot authentificate in openBIS as a user '%s'. Check that the password is correct and that openBIS service URL is correct.%s", + params.getOpenbisUser(), exceptionMsg); + } + +} diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingStateDTO.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingStateDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..16d873fc5a88ffbc3c767c44ddb5a4d45310f16c --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/main/TrackingStateDTO.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.main; + +import ch.systemsx.cisd.common.annotation.BeanProperty; + +public class TrackingStateDTO +{ + private int lastSeenSequencingSampleId; + + private int lastSeenFlowLaneSampleId; + + private int lastSeenDatasetId; + + public int getLastSeenSequencingSampleId() + { + return lastSeenSequencingSampleId; + } + + @BeanProperty(label = "lastSeenSequencingSampleId") + public void setLastSeenSequencingSampleId(int lastSeenSequencingSampleId) + { + this.lastSeenSequencingSampleId = lastSeenSequencingSampleId; + } + + public int getLastSeenFlowLaneSampleId() + { + return lastSeenFlowLaneSampleId; + } + + @BeanProperty(label = "lastSeenFlowLaneSampleId") + public void setLastSeenFlowLaneSampleId(int lastSeenFlowLaneSampleId) + { + this.lastSeenFlowLaneSampleId = lastSeenFlowLaneSampleId; + } + + public int getLastSeenDatasetId() + { + return lastSeenDatasetId; + } + + @BeanProperty(label = "lastSeenDatasetId") + public void setLastSeenDatasetId(int lastSeenDatasetId) + { + this.lastSeenDatasetId = lastSeenDatasetId; + } +} \ No newline at end of file diff --git a/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/utils/LogUtils.java b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/utils/LogUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a9ceb026d76a222ac1453f1763a1693359325773 --- /dev/null +++ b/deep_sequencing_unit/source/java/ch/ethz/bsse/cisd/dsu/tracking/utils/LogUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright 2009 ETH Zuerich, CISD + * + * Licensed 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 ch.ethz.bsse.cisd.dsu.tracking.utils; + +import org.apache.log4j.Logger; + +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.logging.LogCategory; +import ch.systemsx.cisd.common.logging.LogFactory; + +/** + * @author Tomasz Pylak + */ +public class LogUtils +{ + private static final Logger notificationLog = + LogFactory.getLogger(LogCategory.NOTIFY, LogUtils.class); + + private static final Logger operationLog = + LogFactory.getLogger(LogCategory.OPERATION, LogUtils.class); + + public static void notify(EnvironmentFailureException ex) + { + String causeMsg = + (ex.getCause() == null) ? "" : "Error cause: " + ex.getCause().getMessage(); + notificationLog + .error("An environment exception occured why trying to send emails with changes.\n" + + "Check and correct the configuration.\n" + "Error details: " + + ex.getMessage() + "\n" + causeMsg); + } + + public static void notify(Throwable ex) + { + notificationLog + .error("An unexpected exception occured why trying to send emails with changes.\n" + + "Error details: " + ex.getMessage()); + } + + public static EnvironmentFailureException envErr(String message, Throwable exception) + { + String fullMsg = + message + " The following exception has been thrown: " + exception.getMessage(); + return new EnvironmentFailureException(fullMsg, exception); + } + + public static EnvironmentFailureException environmentError(String msgFormat, Object... params) + { + String fullMsg = String.format(msgFormat, params); + return new EnvironmentFailureException(fullMsg); + } +}