-
Notifications
You must be signed in to change notification settings - Fork 1
/
BugzillaImportBean.java
2082 lines (1855 loc) · 85.7 KB
/
BugzillaImportBean.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (c) 2002-2006
* All rights reserved.
*/
package com.atlassian.jira.util;
import com.atlassian.core.ofbiz.CoreFactory;
import com.atlassian.core.user.UserUtils;
import com.atlassian.core.util.collection.EasyList;
import com.atlassian.core.util.map.EasyMap;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.JiraException;
import com.atlassian.jira.action.project.ProjectUtils;
import com.atlassian.jira.bc.project.component.ProjectComponent;
import com.atlassian.jira.bc.project.component.ProjectComponentManager;
import com.atlassian.jira.config.ConstantsManager;
import com.atlassian.jira.config.properties.APKeys;
import com.atlassian.jira.config.properties.ApplicationProperties;
import com.atlassian.jira.exception.CreateException;
import com.atlassian.jira.exception.DataAccessException;
import com.atlassian.jira.external.ExternalUtils;
import com.atlassian.jira.external.beans.ExternalUser;
import com.atlassian.jira.issue.AttachmentManager;
import com.atlassian.jira.issue.CustomFieldManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueFactory;
import com.atlassian.jira.issue.IssueFieldConstants;
import com.atlassian.jira.issue.IssueImpl;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.MutableIssue;
import com.atlassian.jira.issue.attachment.Attachment;
import com.atlassian.jira.issue.cache.CacheManager;
import com.atlassian.jira.issue.comments.CommentManager;
import com.atlassian.jira.issue.context.GlobalIssueContext;
import com.atlassian.jira.issue.customfields.CustomFieldSearcher;
import com.atlassian.jira.issue.customfields.CustomFieldType;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.issue.fields.SummarySystemField;
import com.atlassian.jira.issue.fields.screen.issuetype.IssueTypeScreenSchemeManager;
import com.atlassian.jira.issue.history.ChangeItemBean;
import com.atlassian.jira.issue.history.ChangeLogUtils;
import com.atlassian.jira.issue.index.IndexException;
import com.atlassian.jira.issue.index.IssueIndexManager;
import com.atlassian.jira.issue.link.IssueLinkManager;
import com.atlassian.jira.issue.link.IssueLinkType;
import com.atlassian.jira.issue.link.IssueLinkTypeManager;
import com.atlassian.jira.issue.vote.VoteManager;
import com.atlassian.jira.issue.watchers.WatcherManager;
import com.atlassian.jira.issue.worklog.Worklog;
import com.atlassian.jira.issue.worklog.WorklogImpl;
import com.atlassian.jira.issue.worklog.WorklogManager;
import com.atlassian.jira.permission.PermissionSchemeManager;
import com.atlassian.jira.project.Project;
import com.atlassian.jira.project.ProjectManager;
import com.atlassian.jira.project.version.Version;
import com.atlassian.jira.project.version.VersionManager;
import com.atlassian.jira.scheme.SchemeManager;
import com.atlassian.jira.security.PermissionManager;
import com.atlassian.jira.security.Permissions;
import com.atlassian.jira.user.util.UserUtil;
import com.atlassian.jira.web.action.admin.customfields.CreateCustomField;
import com.atlassian.jira.web.action.util.BugzillaConnectionBean;
import com.atlassian.jira.workflow.WorkflowFunctionUtils;
import com.atlassian.jira.security.roles.ProjectRoleImpl;
import com.atlassian.jira.security.roles.DefaultProjectRoleManager;
import com.opensymphony.user.EntityNotFoundException;
import com.opensymphony.user.User;
import com.opensymphony.util.TextUtils;
import org.apache.commons.collections.set.ListOrderedSet;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternMatcher;
import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.apache.oro.text.regex.Substitution;
import org.apache.oro.text.regex.Util;
import org.ofbiz.core.entity.GenericDelegator;
import org.ofbiz.core.entity.GenericEntityException;
import org.ofbiz.core.entity.GenericValue;
import org.ofbiz.core.util.UtilDateTime;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* REQUIRED:
* trackers_attachment: mimetype, attachment_data
* trackers_post: post_text_wiki (wiki representation of post_text)
*/
public class BugzillaImportBean
{
private static final Logger log4jLog = Logger.getLogger(BugzillaImportBean.class);
private static final String PHPBB_CHANGE_ITEM_FIELD = "phpBB Import Key";
// Nils' user id to map as component/project lead
private static final int PHPBB_PROJECTS_LEADER_ID = 75126;
// Nils' username in case we need it directly
private static final String PHPBB_PROJECTS_LEADER_NAME = "naderman";
private final IssueIndexManager indexManager;
private final GenericDelegator genericDelegator;
private final ProjectManager projectManager;
private final SchemeManager permissionSchemeManager;
private final CacheManager cacheManager;
private final VersionManager versionManager;
private final VoteManager voteManager;
private final ProjectComponentManager projectComponentManager;
private final IssueManager issueManager;
private final AttachmentManager attachmentManager;
private final IssueTypeScreenSchemeManager issueTypeScreenSchemeManager;
private final CustomFieldManager customFieldManager;
private final PermissionManager permissionManager;
private final IssueLinkManager issueLinkManager;
private final IssueLinkTypeManager issueLinkTypeManager;
private final ConstantsManager constantsManager;
private final ExternalUtils externalUtils;
private final CommentManager commentManager;
private final ApplicationProperties applicationProperties;
private final WatcherManager watcherManager;
private final UserUtil userUtil;
private final Set truncSummaryIssueKeys = new ListOrderedSet();
StringBuffer importLog = null;
//these are cached lookups to save us having to go to the database each time.
private final Map userKeys = new HashMap(255);
// Map of lowercase'd project names to JIRA Project GVs
private final Map projectKeys = new HashMap();
// Map from phpBB user id to Jira User
private final Map versionKeys = new HashMap();
private final Map componentKeys = new HashMap();
// phpBB -> jira issue key mappings
private Map previouslyImportedKeys = new HashMap(); // issues imported at any time (this or earlier runs)
private Map importedKeys = new HashMap(); // Map of phpBB ids (Integer) to Jira ids (Long) of issues imported during this run
private String selectedProjects;
private User importer;
private BugzillaMappingBean bugzillaMappingBean;
private boolean reuseExistingUsers;
private boolean workHistory;
private final Map projectToPhpBBIdMap = new HashMap();
private boolean onlyNewIssues;
public static final String BUGZILLA_ID_TYPE = "importid";
public static final String BUGZILLA_ID_SEARCHER = "exactnumber";
public static final String BUGZILLA_ID_CF_NAME = "phpBB Import Key";
private CustomField phpBBIdCustomField;
private PreparedStatement profilePS;
private PreparedStatement componentPS;
private PreparedStatement projectPS;
private PreparedStatement commentPS;
private PreparedStatement ticketDescriptionPS;
private final IssueFactory issueFactory;
private final WorklogManager worklogManager;
public BugzillaImportBean(final IssueIndexManager indexManager, final GenericDelegator genericDelegator, final ProjectManager projectManager, final PermissionSchemeManager permissionSchemeManager, final CacheManager cacheManager, final VersionManager versionManager, final VoteManager voteManager, final ProjectComponentManager projectComponentManager, final CustomFieldManager customFieldManager, final IssueManager issueManager, final AttachmentManager attachmentManager, final IssueTypeScreenSchemeManager issueTypeScreenSchemeManager, final PermissionManager permissionManager, final IssueLinkManager issueLinkManager, final IssueLinkTypeManager issueLinkTypeManager, final ConstantsManager constantsManager, final ExternalUtils externalUtils, final CommentManager commentManager, final IssueFactory issueFactory, final WorklogManager worklogManager, final ApplicationProperties applicationProperties, final WatcherManager watcherManager, final UserUtil userUtil)
{
this.indexManager = indexManager;
this.genericDelegator = genericDelegator;
this.projectManager = projectManager;
this.permissionSchemeManager = permissionSchemeManager;
this.cacheManager = cacheManager;
this.versionManager = versionManager;
this.voteManager = voteManager;
this.projectComponentManager = projectComponentManager;
this.customFieldManager = customFieldManager;
this.issueManager = issueManager;
this.attachmentManager = attachmentManager;
this.issueTypeScreenSchemeManager = issueTypeScreenSchemeManager;
this.permissionManager = permissionManager;
this.issueLinkManager = issueLinkManager;
this.issueLinkTypeManager = issueLinkTypeManager;
this.constantsManager = constantsManager;
this.externalUtils = externalUtils;
this.commentManager = commentManager;
this.issueFactory = issueFactory;
this.worklogManager = worklogManager;
this.applicationProperties = applicationProperties;
this.watcherManager = watcherManager;
this.userUtil = userUtil;
}
/**
* This method will determine all the users that will need to exist in JIRA to successfully import the
* specified projects and will return the users that do not yet exist.
*
* @param connectionBean initialized connection bean
* @param projectNames the projects, by phpBB project name, that you want to import.
* @return Set <ExternalUser> all the users that will need to exist in JIRA but do not yet.
*/
public Set getNonExistentAssociatedUsers(final BugzillaConnectionBean connectionBean, final String[] projectNames)
{
return ImportUtils.getNonExistentUsers(getAssociatedUsers(connectionBean, projectNames));
}
/**
* Main method of this bean. Creates JIRA projects mirroring those found in a phpBB database.
*
* @param phpBBMappingBean Mappings from phpBB to JIRA, including project key, statuses, etc
* @param connectionBean Bugzilla connection bean
* @param enableNotifications Whether to send email notifications for newly created issues
* @param reuseExistingUsers Do we try to reuse existing users, or create a unique user for every phpBB user?
* @param onlyNewIssues Should we only import issues that haven't previously been imported (to avoid duplicates)?
* @param reindex Whether to reindex after the import
* @param workHistory Whether to import work history as well
* @param projectNames Array of phpBB project names to import
* @param importer User performing the import operation
* @throws Exception if something goes wrong
*/
public void create(final BugzillaMappingBean bugzillaMappingBean, final BugzillaConnectionBean connectionBean, final boolean enableNotifications, final boolean reuseExistingUsers, final boolean onlyNewIssues, final boolean reindex, final boolean workHistory, final String[] projectNames, final User importer) throws Exception
{
importLog = new StringBuffer(1024 * 30);
if (projectNames.length == 0)
{
log("No projects selected for import");
return;
}
this.bugzillaMappingBean = bugzillaMappingBean;
this.reuseExistingUsers = reuseExistingUsers;
this.onlyNewIssues = onlyNewIssues;
this.workHistory = workHistory;
//todo - clear userKeys, projectKeys etc
this.importer = importer;
try
{
final long starttime = System.currentTimeMillis();
createOrFindCustomFields();
selectedProjects = ImportUtils.getSQLTokens(projectNames);
final Connection conn = connectionBean.getConnection();
createPreparedStatements(conn);
ImportUtils.setSubvertSecurityScheme(true);
if (reindex)
{
ImportUtils.setIndexIssues(false);
}
ImportUtils.setEnableNotifications(enableNotifications);
createProjects(projectNames, conn);
createVersions(conn);
createComponents(conn);
// if non-lazy: createUsers();
createIssues(conn);
rewriteBugLinks();
ImportUtils.setSubvertSecurityScheme(false); // before the reindex, so pico-instantiated-during-reindex components don't get the wrong thing. JRA-7638
if (reindex)
{
ImportUtils.setIndexIssues(true);
}
// Votes are not used in the phpBB.com database
// createVotes(conn);
createWatchers(conn);
if (reindex)
{
log("Reindexing (this may take a while)...");
indexManager.reIndexAll();
}
final long endtime = System.currentTimeMillis();
log("\nImport SUCCESS and took: " + (endtime - starttime) + " ms.");
}
finally
{
closePreparedStatements();
connectionBean.closeConnection();
ImportUtils.setSubvertSecurityScheme(false); // do again just in case we failed before the reindex
if (reindex)
{
ImportUtils.setIndexIssues(true);
}
if (!enableNotifications)
{
ImportUtils.setEnableNotifications(true);
}
}
}
private void createPreparedStatements(final Connection conn) throws SQLException
{
// SQL query to get component name from id
componentPS = conn.prepareStatement("select component_name from trackers_component where component_id = ?");
// Get Project name
projectPS = conn.prepareStatement("select project_name from trackers_project where project_id = ?");
// Prepared Statement for profiles
profilePS = conn.prepareStatement("SELECT user_id, username, user_email FROM community_users WHERE user_id = ?");
// Get comment by ticket id
// We get all ticket posts (and exclude the one for the ticket itself later)
// We do not import private tickets
commentPS = conn.prepareStatement("SELECT * FROM trackers_post WHERE ticket_id = ? ORDER BY post_timestamp ASC");
// Ticket description
ticketDescriptionPS = conn.prepareStatement("SELECT p.* FROM trackers_ticket as t, trackers_post as p WHERE t.ticket_id = ? AND p.post_id = t.post_id");
}
private void closePreparedStatements() throws SQLException
{
if (componentPS != null)
{
componentPS.close();
}
if (projectPS != null)
{
projectPS.close();
}
if (profilePS != null)
{
profilePS.close();
}
if (commentPS != null)
{
commentPS.close();
}
if (ticketDescriptionPS != null)
{
ticketDescriptionPS.close();
}
}
private void createIssues(final Connection conn) throws Exception
{
int count = 0;
log("\n\nImporting Issues from project(s) " + selectedProjects);
// use the changeItem importLog to retrieve the list of issues previously imported from phpBB
previouslyImportedKeys = retrieveImportedIssues();
String sql = "SELECT t.*, s.severity_name, v.version_name, v2.version_name as fixed_in_version, st.status_name FROM (trackers_ticket t, trackers_version v, trackers_status as st) LEFT JOIN trackers_severity s ON (s.severity_id = t.severity_id) LEFT JOIN trackers_version v2 ON (t.version_fixed = v2.version_id) where st.status_id = t.status_id AND t.version_id = v.version_id AND t.ticket_private = 0 AND t.project_id in (" + commaSeparate(projectToPhpBBIdMap.values()) + ") ORDER BY t.ticket_id ASC ";
final PreparedStatement preparedStatement = conn.prepareStatement(sql);
final ResultSet resultSet = preparedStatement.executeQuery();
importedKeys = new HashMap();
// ADD mimetype and attachment_data to table...
final PreparedStatement attachPrepStatement = conn.prepareStatement("SELECT a.attachment_id, a.attachment_size, a.attachment_title, a.mimetype, a.attachment_data, p.user_id, p.created_ts FROM trackers_attachment as a, trackers_post as p WHERE a.attachment_private = 0 AND a.post_id = p.post_id AND p.ticket_id = ? AND p.post_private = 0 ORDER BY a.attachment_id ASC");
// Ticket 'dupe' is a duplicate of ticket 'dupe_of' - inward
final PreparedStatement linkDuplicatesStatement = conn.prepareStatement("SELECT ticket_id as dupe FROM trackers_ticket WHERE duplicate_id = ? AND duplicate_id > 0");
// Ticket 'dupe_of' duplicates Ticket 'dupe' - outward
final PreparedStatement linkDuplicatedOfStatement = conn.prepareStatement("SELECT duplicate_id as dupe_of FROM trackers_ticket WHERE ticket_id = ? AND duplicate_id > 0");
final IssueLinkType dependencyLinkType = createOrFindLinkType("Dependency", "depends on", "blocks");
final IssueLinkType duplicateLinkType = createOrFindLinkType("Duplicate", "duplicates", "is duplicated by");
truncSummaryIssueKeys.clear();
while (resultSet.next())
{
final int bugId = resultSet.getInt("ticket_id");
if (!onlyNewIssues || !previouslyImportedKeys.containsKey(new Integer(resultSet.getInt("ticket_id"))))
{
log("Importing Ticket ID " + bugId);
String componentName;
try
{
componentName = resultSet.getString("component_name");
}
catch (final SQLException e)
{
componentName = getComponentName(resultSet.getInt("component_id"));
}
try
{
final GenericValue issue = createIssue(resultSet, getProjectName(resultSet, true), componentName);
createCommentAndDescription(bugId, issue);
// NOTE: this call has not been tested, we are waiting for test data, that is why it is surrounded
// in a conditional
// phpBB does not have a work history (hours worked on bugs)
if (workHistory)
{
// createWorkHistory(conn, bugId, issueFactory.getIssue(issue));
}
createAttachments(conn, attachPrepStatement, bugId, issue);
if (applicationProperties.getOption(APKeys.JIRA_OPTION_ISSUELINKING))
{
// createLinks(dependencyLinkType, "blocked", "dependson", linkBlocksPrepStatement, linkDependsOnPrepStatement, bugId, issue);
createLinks(duplicateLinkType, "dupe", "dupe_of", linkDuplicatesStatement, linkDuplicatedOfStatement, bugId, issue);
}
else
{
// NOTE: should not occur, i enabled issue linking for duplicates (Meik)
log("Issue links will not be imported from phpBB since issue linking is disabled in JIRA.");
}
}
catch (final Exception e)
{
log("Exception processing bug id " + bugId);
throw (e);
}
count++;
}
else
{
log("Not re-importing Ticket ID " + bugId);
}
}
log(count + " issues imported from phpBB.");
ImportUtils.closePS(preparedStatement);
ImportUtils.closePS(attachPrepStatement);
// ImportUtils.closePS(linkBlocksPrepStatement);
// ImportUtils.closePS(linkDependsOnPrepStatement);
}
private String getComponentName(final int componentId) throws SQLException
{
componentPS.setInt(1, componentId);
final ResultSet rs = componentPS.executeQuery();
rs.next();
final String name = rs.getString("component_name");
rs.close();
return name;
}
private GenericValue createIssue(final ResultSet resultSet, final String projectName, final String componentName) throws IndexException, SQLException, GenericEntityException, CreateException
{
final Map fields = new HashMap();
final MutableIssue issueObject = IssueImpl.getIssueObject(null);
issueObject.setProject(getProject(projectName));
issueObject.setReporter(getUser(resultSet.getInt("user_id")));
final int assignedUser = resultSet.getInt("assigned_user");
if (assignedUser > 0)
{
issueObject.setAssignee(getUser(assignedUser));
}
/* if (resultSet.getString("bug_severity").equals("enhancement"))
{
issueObject.setIssueTypeId(getEnhancementIssueTypeId());
}
else
{
issueObject.setIssueTypeId(getBugIssueTypeId());
}*/
issueObject.setIssueTypeId(getBugIssueTypeId());
// truncate summary if necessary - JRA-12837
final int summaryMaxLength = SummarySystemField.MAX_LEN.intValue();
String summary = StringEscapeUtils.unescapeHtml(resultSet.getString("ticket_title"));
final boolean isSummaryTruncated;
if (summary.length() > summaryMaxLength)
{
summary = summary.substring(0, summaryMaxLength);
isSummaryTruncated = true;
}
else
{
isSummaryTruncated = false;
}
issueObject.setSummary(summary);
// Make sure that the priority is in lower case. JRA-9586
String priorityString = resultSet.getString("severity_name");
if (priorityString != null)
{
priorityString = priorityString.toLowerCase();
}
issueObject.setPriorityId(bugzillaMappingBean.getPriority(priorityString));
final StringBuffer environment = new StringBuffer();
String ticket_php = resultSet.getString("ticket_php");
String ticket_dbms = resultSet.getString("ticket_dbms");
if (ticket_php != null || ticket_dbms != null)
{
environment.append("PHP Environment: ").append(ticket_php).append("\nDatabase: ").append(ticket_dbms);
}
/* final String url = resultSet.getString("bug_file_loc");
if (!"".equals(url))
{
environment.append("\nURL: ").append(url);
}*/
if (!"".equals(environment.toString()))
{
issueObject.setEnvironment(environment.toString());
}
// setup the associations with components/versions
final String version = resultSet.getString("version_name");
final String fixversion = resultSet.getString("fixed_in_version");
createVersionComponentAssociations(issueObject, projectName, version, componentName, fixversion);
// NOTE: this call has not been tested, we are waiting for test data, that is why it is surrounded
// in a conditional
/* if (workHistory && !resultSet.getString("estimated_time").equals(""))
{
long time_original_estimate, time_remaining;
time_original_estimate = (long) (3600.0 * resultSet.getFloat("estimated_time"));
time_remaining = (long) (3600.0 * resultSet.getFloat("remaining_time"));
issueObject.setOriginalEstimate(new Long(time_original_estimate));
issueObject.setTimeSpent(new Long(time_remaining));
}*/
fields.put("issue", issueObject);
final GenericValue origianlIssueGV = ComponentManager.getInstance().getIssueManager().getIssue(issueObject.getId());
fields.put(WorkflowFunctionUtils.ORIGINAL_ISSUE_KEY, IssueImpl.getIssueObject(origianlIssueGV));
final GenericValue issue = issueManager.createIssue(importer, fields);
if (isSummaryTruncated)
{
truncSummaryIssueKeys.add(issue.getString("key"));
}
final String phpBBStatus = resultSet.getString("status_name").toLowerCase();
String jiraBugStatus = bugzillaMappingBean.getStatus(phpBBStatus);
boolean foundStatus = true;
// JRA-10017 - always fall back to the open status if we can't find the correct status
if (jiraBugStatus == null)
{
foundStatus = false;
jiraBugStatus = String.valueOf(IssueFieldConstants.OPEN_STATUS_ID);
}
issue.set(IssueFieldConstants.STATUS, jiraBugStatus);
// make sure no resolution if the issue is unresolved
if (!"5".equals(jiraBugStatus) && !"6".equals(jiraBugStatus))
{
issue.set(IssueFieldConstants.RESOLUTION, null);
}
else
{
final String resolution = bugzillaMappingBean.getResolution(resultSet.getString("status_name").toLowerCase());
issue.set(IssueFieldConstants.RESOLUTION, resolution);
//If the issue is resolved, also set the resolution date (the mapping may return null meaning unresolved).
//We'll use the last updated time for this, since phpBB doesn't seem to store a resolution date.
if(resolution != null)
{
issue.set(IssueFieldConstants.RESOLUTION_DATE, resultSet.getTimestamp("delta_ts"));
}
}
issue.set(IssueFieldConstants.CREATED, resultSet.getTimestamp("created_ts"));
//Previously the import always set the updated date to the time of the import. This has been
//changed to use the last updated time from the database.
issue.set(IssueFieldConstants.UPDATED, resultSet.getTimestamp("delta_ts"));
issue.store();
setCurrentWorkflowStep(issue);
final int TicketId = resultSet.getInt("ticket_id");
createChangeHistory(TicketId, issue);
previouslyImportedKeys.put(new Integer(TicketId), issue.getLong("id"));
importedKeys.put(new Integer(TicketId), issue.getLong("id"));
// Create custom field value for the issue
if (phpBBIdCustomField != null)
{
phpBBIdCustomField.createValue(IssueImpl.getIssueObject(issue), new Double(TicketId));
indexManager.reIndex(issue);
}
else
{
log("phpBB Id customfield not found. phpBB Id not added.");
}
if (!foundStatus)
{
log("Creating issue: " + issue.getString("key") + " for phpBB issue: " + TicketId + " we could not find a mapping for phpBB status " + phpBBStatus + ", defaulting to JIRA status Open");
}
return issue;
}
private String getEnhancementIssueTypeId()
{
if (constantsManager.getIssueType(bugzillaMappingBean.JIRA_ENHANCEMENT_ISSUE_TYPE_ID) != null)
{
return bugzillaMappingBean.JIRA_ENHANCEMENT_ISSUE_TYPE_ID;
}
else
{
log("ERROR: JIRA does not have an enhancement issue type with id " + bugzillaMappingBean.JIRA_ENHANCEMENT_ISSUE_TYPE_ID + "; creating as Bug instead");
return getBugIssueTypeId();
}
}
private String getBugIssueTypeId()
{
if (constantsManager.getIssueType(bugzillaMappingBean.JIRA_BUG_ISSUE_TYPE_ID) != null)
{
return bugzillaMappingBean.JIRA_BUG_ISSUE_TYPE_ID;
}
else
{
final Collection issueTypes = constantsManager.getIssueTypes();
if (issueTypes.isEmpty())
{
throw new RuntimeException("No JIRA issue types defined!");
}
final String firstIssueType = ((GenericValue) issueTypes.iterator().next()).getString("id");
log("ERROR: JIRA does not have a bug issue type with id " + bugzillaMappingBean.JIRA_BUG_ISSUE_TYPE_ID + "; using first found issue type " + firstIssueType + " instead.");
return firstIssueType;
}
}
/**
* Associate the issue with a single version and component. This is ok, as phpBB only allows for a single
* version and component for an issue.
*
* @param issue issue
* @param project project
* @param version affects version
* @param component component
* @param fixVersion fix version
*/
private void createVersionComponentAssociations(final MutableIssue issue, final String project, final String version, final String component, final String fixVersion)
{
final Version verKey = getVersion(project + ":" + version);
final Version fixverKey = getVersion(project + ":" + fixVersion);
if (verKey != null)
{
final Version affectsVersion = versionManager.getVersion(verKey.getLong("id"));
issue.setAffectedVersions(EasyList.build(affectsVersion));
}
else
{
if (log4jLog.isEnabledFor(Priority.ERROR))
{
log4jLog.error("Could not find version '" + project + ":" + version + "' to associate with issue " + issue);
}
}
if (fixverKey != null)
{
final Version fixVer = versionManager.getVersion(fixverKey.getLong("id"));
issue.setFixVersions(EasyList.build(fixVer));
}
else
{
// Ignore, no fix present...
}
final GenericValue comp = getComponent(project + ":" + component);
if (comp != null)
{
final GenericValue affectsComponent = projectManager.getComponent(comp.getLong("id"));
issue.setComponents(EasyList.build(affectsComponent));
}
else
{
if (log4jLog.isEnabledFor(Priority.ERROR))
{
log4jLog.error("Could not find component " + project + ":" + component + " to associate with issue " + issue);
}
}
}
/**
* Given an issue, update the underlying workflow, so that it matches the issues status.
*
* @param issue issue generic value
* @throws GenericEntityException if workflow step fails to be persisted
*/
private void setCurrentWorkflowStep(final GenericValue issue) throws GenericEntityException
{
// retrieve the wfCurrentStep for this issue and change it
final Collection wfCurrentStepCollection = genericDelegator.findByAnd("OSCurrentStep", EasyMap.build("entryId", issue.getLong("workflowId")));
final GenericValue wfCurrentStep = (GenericValue) getOnly(wfCurrentStepCollection);
wfCurrentStep.set("stepId", bugzillaMappingBean.getWorkflowStep(issue.getString("status")));
wfCurrentStep.set("status", bugzillaMappingBean.getWorkflowStatus(issue.getString("status")));
wfCurrentStep.store();
}
private void createCommentAndDescription(final int bug_id, final GenericValue issue) throws Exception
{
// Get description and description id for this ticket
String description = null;
int postid = 0;
ticketDescriptionPS.setInt(1, bug_id);
final ResultSet DescriptionResultSet = ticketDescriptionPS.executeQuery();
if (DescriptionResultSet.next())
{
// @todo introduce new column for HTML? I do not think JIRA is able to parse BBCode. ;)
description = DescriptionResultSet.getString("post_text_wiki");
postid = DescriptionResultSet.getInt("post_id");
}
DescriptionResultSet.close();
commentPS.setInt(1, bug_id);
final ResultSet resultSet = commentPS.executeQuery();
while (resultSet.next())
{
// Skip if the comment is the original description post_id
if (resultSet.getInt("post_id") == postid)
{
}
else
{
final User user = getUser(resultSet.getInt("user_id"));
/* check permissions first
if (!permissionManager.hasPermission(Permissions.COMMENT_ISSUE, issue, user))
{
log("You (" + user.getFullName() + ") do not have permission to comment on an issue in project: " + projectManager.getProjectObj(
issue.getLong("project")).getName());
}
else
{*/
final String author = user.getName();
if (resultSet.getInt("post_private") == 1)
{
// Private post only visible by the developers role...
// final DefaultProjectRoleManager ProjectRole = null;
// commentManager.create(issueFactory.getIssue(issue), author, author, resultSet.getString("post_text_wiki"), null, ProjectRole.getProjectRole("Developers").getId(), resultSet.getTimestamp("created_ts"), resultSet.getTimestamp("created_ts"), false, false);
final long RoleId = (long) 10001;
commentManager.create(issueFactory.getIssue(issue), author, author, resultSet.getString("post_text_wiki"), null, RoleId, resultSet.getTimestamp("created_ts"), resultSet.getTimestamp("created_ts"), false, false);
}
else
{
commentManager.create(issueFactory.getIssue(issue), author, author, resultSet.getString("post_text_wiki"), null, null, resultSet.getTimestamp("created_ts"), resultSet.getTimestamp("created_ts"), false, false);
}
// }
}
}
resultSet.close();
issue.set("description", description);
issue.store();
cacheManager.flush(CacheManager.ISSUE_CACHE, issue); // Flush the cache, otherwise later when we look up the issue we'll get something stale. JRA-5542
}
/* NOTE: this is untested code submitted by Vincent Fiano, we still need some test data to run through this
private void createWorkHistory(final Connection conn, final int bug_id, final Issue issue) throws SQLException, JiraException
{
final PreparedStatement preparedStatement = conn.prepareStatement("SELECT * FROM bugs_activity WHERE bug_id = ? AND fieldid = 45 ORDER BY bug_when ASC");
preparedStatement.setInt(1, bug_id);
final ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next())
{
final User user = getUser(resultSet.getInt("who"));
log("Adding work history for bug " + bug_id + ": " + new Float(resultSet.getFloat("added")) + " hours worked by " + getUser(resultSet.getInt("who")) + " on " + resultSet.getTimestamp("bug_when"));
final Worklog worklog = new WorklogImpl(worklogManager, issue, null, user.getName(),
"(see comment dated " + resultSet.getTimestamp("bug_when") + ")", resultSet.getTimestamp("bug_when"), null, null, new Long(
(long) (3600.0 * resultSet.getFloat("added"))));
worklogManager.create(user, worklog, null, false);
}
}*/
/**
* Store the original phpBB bug id in the change history.
*
* @param bug_id bug id
* @param issue issue
*/
private void createChangeHistory(final int bug_id, final GenericValue issue)
{
// create a change group and change item for each issue imported to record the original phpBB id.
// change items used to make sure issues are not duplicated
final List changeItems = EasyList.build(new ChangeItemBean(ChangeItemBean.STATIC_FIELD, PHPBB_CHANGE_ITEM_FIELD, null,
Integer.toString(bug_id), null, issue.getLong("id").toString()));
ChangeLogUtils.createChangeGroup(importer, issue, issue, changeItems, true);
}
private void createWatchers(final Connection conn) throws SQLException
{
log("\n\nImporting Watchers");
int count = 0;
final PreparedStatement preparedStatement = conn.prepareStatement("SELECT user_id FROM trackers_ticket_watch WHERE ticket_id = ? AND user_id <> 1");
final Iterator phpBBBugIdIter = previouslyImportedKeys.keySet().iterator();
// for each imported bug..
while (phpBBBugIdIter.hasNext())
{
final Integer phpBBBugId = (Integer) phpBBBugIdIter.next();
preparedStatement.setInt(1, phpBBBugId.intValue());
final ResultSet rs = preparedStatement.executeQuery();
// for each watcher of an imported bug..
while (rs.next())
{
try
{
final User watcher = getUser(rs.getInt("user_id")); // find or create the watcher
final Long jiraBugId = (Long) previouslyImportedKeys.get(phpBBBugId);
final GenericValue issue = issueManager.getIssue(jiraBugId);
watcherManager.startWatching(watcher, issue);
count++;
}
catch (final SQLException e)
{
final String err = "Failed to add a watcher to issue with phpBB id '" + phpBBBugId + "': " + e.getMessage();
log(err);
log4jLog.warn(err, e);
}
catch (final RuntimeException e)
{
final String err = "Failed to add a watcher to issue with phpBB id '" + phpBBBugId + "': " + e.getMessage();
log(err);
log4jLog.warn(err, e);
}
}
}
ImportUtils.closePS(preparedStatement);
log(count + " watchers imported from phpBB.");
}
/**
* Return a map of phpBBKey (Integer) -> Jira Issues Id (Integer).
* <p/>
* It does this by looking through the change items for the phpBB import key.
*
* @return map of previously imported keys (old to new)
* @throws GenericEntityException if cannot read from change items
*/
protected Map retrieveImportedIssues() throws GenericEntityException
{
final Map previousKeys = new HashMap();
// get the issues previously imported from phpBB via the change items.
final Collection changeItems = genericDelegator.findByAnd("ChangeItem", EasyMap.build("field", PHPBB_CHANGE_ITEM_FIELD));
for (final Iterator iterator = changeItems.iterator(); iterator.hasNext();)
{
final GenericValue changeItem = (GenericValue) iterator.next();
previousKeys.put(new Integer(changeItem.getString("oldstring")), new Long(changeItem.getString("newstring")));
}
return previousKeys;
}
private void createComponents(final Connection conn) throws SQLException
{
int componentCount = 0;
log("\n\nImporting Components from project(s) " + selectedProjects + "\n");
final PreparedStatement preparedStatement = conn.prepareStatement("SELECT * FROM trackers_component where project_id in (" + commaSeparate(projectToPhpBBIdMap.values()) + ") ");
final ResultSet resultSet = preparedStatement.executeQuery();
String componentLead = null;
String component = null;
while (resultSet.next())
{
try
{
// lookup the component lead (only available in Enterprise)
componentLead = getComponentLead(PHPBB_PROJECTS_LEADER_ID);
component = resultSet.getString("component_name");
}
catch (final SQLException ex)
{
if (component != null)
{
final String err = "Failed to retreive the default assignee of component '" + component + "'";
log(err);
log4jLog.warn(err, ex);
}
else
{
final String err = "Failed to retrieve a component from phpBB";
log(err);
log4jLog.error(err, ex);
}
}
log("Importing Component: " + component);
final boolean created = createComponent(getProjectName(resultSet, false), component, componentLead, resultSet.getString("component_name"));
if (created)
{
componentCount++;
}
}
log(componentCount + " components imported from PhpBB.");
ImportUtils.closePS(preparedStatement);
}
private String getComponentLead(final int defaultAssigneeId) throws SQLException
{
String componentLead = null;
profilePS.clearParameters();
profilePS.setInt(1, defaultAssigneeId);
final ResultSet componentLeadResultSet = profilePS.executeQuery();
if (componentLeadResultSet.next())
{
componentLead = componentLeadResultSet.getString("username");
componentLead = StringEscapeUtils.unescapeHtml(componentLead);
}
return componentLead;
}
/**
* Handles the different database schemata to retrieve the product name.
* The program and product columns have been replaced with a FK pointing to
* the name in the products table in 2.17.
* <p/>
* In 2.16, the product name for BUGS are listed in the product column
* For COMPONENTS and VERSIONS, they are listed in the program column
*
* @param resultSet result set containing product information
* @param isPhpBBBug is phpBB flag
* @return product name
* @throws SQLException if reading from result set fails
*/
private String getProjectName(final ResultSet resultSet, final boolean isPhpBBBug) throws SQLException
{
String projectName;
try
{
// 2.17+ format
final int pid = resultSet.getInt("project_id");
if (pid == 0)
{
throw new RuntimeException("Null project_id for " + resultSet);
}
projectPS.setInt(1, pid);
final ResultSet rs = projectPS.executeQuery();
final boolean hasNext = rs.next();
if (!hasNext)
{
throw new RuntimeException("No project with ID " + pid);
}
projectName = rs.getString("project_name");
rs.close();
}
catch (final SQLException e)
{
projectName = resultSet.getString("project_name");
}
return projectName;
}
private boolean createComponent(final String projectName, final String componentName, final String componentLead, final String description)
{
final GenericValue project = getProject(projectName);
final GenericValue existingComponent = projectManager.getComponent(project, componentName);
// if the componentName exists already, do not import
if (existingComponent != null)
{
log("Component " + componentName + " in Project: " + projectName + " already exists. Not imported");
componentKeys.put(projectName + ":" + componentName, existingComponent);
return false;
}
else
{
try
{
final ProjectComponent projectComponent = projectComponentManager.create(componentName, description, componentLead, 0,
project.getLong("id"));
final GenericValue componentGV = projectComponentManager.convertToGenericValue(projectComponent);
// imported components are stored for use later
componentKeys.put(projectName + ":" + componentName, componentGV);
return true;
}
catch (final Exception e)
{