Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormkuppe2010-09-30 13:59:16 +0000
committermkuppe2010-09-30 13:59:16 +0000
commit5685d925d69dca42da86d382a9418d769b52423c (patch)
tree064c23415b4cb18e23a6e783eb0c57c7bdd17969 /providers
parent0684504e0b0da56917b85f3c4dd46f6185009629 (diff)
downloadorg.eclipse.ecf-5685d925d69dca42da86d382a9418d769b52423c.tar.gz
org.eclipse.ecf-5685d925d69dca42da86d382a9418d769b52423c.tar.xz
org.eclipse.ecf-5685d925d69dca42da86d382a9418d769b52423c.zip
NEW - bug 326228: [Discovery][JmDNS] Upgrade to JmDNS 3.xJmDNS3-2-2_integrated
https://bugs.eclipse.org/bugs/show_bug.cgi?id=326228
Diffstat (limited to 'providers')
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/.classpath2
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.core.prefs28
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.ui.prefs11
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.ltk.core.refactoring.prefs3
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/META-INF/MANIFEST.MF12
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/build.properties3
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/JmDNS.java271
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceEvent.java44
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceInfo.java579
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceListener.java30
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceTypeListener.java19
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSCache.java642
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSConstants.java127
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSEntry.java342
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSIncoming.java734
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSListener.java15
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSMessage.java267
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSOutgoing.java661
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSQuestion.java339
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSRecord.java968
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSState.java111
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSStatefulObject.java529
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/HostInfo.java391
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/JmDNSImpl.java3451
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ListenerStatus.java330
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceEventImpl.java86
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceInfoImpl.java1205
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/SocketListener.java78
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSConstants.java59
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSLabel.java97
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOperationCode.java95
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOptionCode.java87
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordClass.java153
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordType.java325
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSResultCode.java161
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSState.java193
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Announcer.java159
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Canceler.java122
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/DNSTask.java249
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Prober.java197
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/RecordReaper.java100
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Renewer.java154
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Responder.java347
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceInfoResolver.java101
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceResolver.java101
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/TypeResolver.java92
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java138
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java119
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceResolver.java88
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/TypeResolver.java84
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Announcer.java162
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Canceler.java159
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/DNSStateTask.java217
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Prober.java181
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Renewer.java157
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/src/org/eclipse/ecf/internal/provider/jmdns/SimpleFIFOQueue.java1
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.jmdns/src/org/eclipse/ecf/provider/jmdns/container/JMDNSDiscoveryContainer.java11
57 files changed, 11037 insertions, 4350 deletions
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/.classpath b/providers/bundles/org.eclipse.ecf.provider.jmdns/.classpath
index 800163ad3..481a7ec48 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/.classpath
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/CDC-1.1%Foundation-1.1"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="jmdns"/>
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.core.prefs b/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.core.prefs
index 7fcfd0da0..b92ff31fb 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.core.prefs
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.core.prefs
@@ -1,4 +1,4 @@
-#Mon May 04 17:44:39 PDT 2009
+#Thu Sep 30 15:01:01 CEST 2010
eclipse.preferences.version=1
org.eclipse.jdt.core.builder.cleanOutputFolder=clean
org.eclipse.jdt.core.builder.duplicateResourceTask=warning
@@ -7,8 +7,8 @@ org.eclipse.jdt.core.builder.resourceCopyExclusionFilter=*.launch
org.eclipse.jdt.core.circularClasspath=error
org.eclipse.jdt.core.classpath.exclusionPatterns=enabled
org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.4
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
@@ -37,7 +37,7 @@ org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
-org.eclipse.jdt.core.compiler.problem.invalidJavadoc=error
+org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore
org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled
org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled
@@ -54,6 +54,7 @@ org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled
org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
@@ -69,6 +70,7 @@ org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
+org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
@@ -85,6 +87,7 @@ org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverridin
org.eclipse.jdt.core.compiler.problem.unusedImport=error
org.eclipse.jdt.core.compiler.problem.unusedLabel=ignore
org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore
+org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=enabled
@@ -92,9 +95,10 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=enab
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
-org.eclipse.jdt.core.compiler.source=1.3
+org.eclipse.jdt.core.compiler.source=1.5
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
@@ -105,6 +109,7 @@ org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
@@ -149,10 +154,15 @@ org.eclipse.jdt.core.formatter.comment.indent_root_tags=false
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
+org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
+org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
@@ -164,6 +174,10 @@ org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
@@ -332,6 +346,8 @@ org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.join_lines_in_comments=true
+org.eclipse.jdt.core.formatter.join_wrapped_lines=true
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
@@ -344,7 +360,9 @@ org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
+org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
+org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
org.eclipse.jdt.core.incompatibleJDKLevel=ignore
org.eclipse.jdt.core.incompleteClasspath=error
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.ui.prefs b/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.ui.prefs
index 9bb86c1d3..cc4144860 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.ui.prefs
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.jdt.ui.prefs
@@ -1,7 +1,6 @@
-#Wed Oct 10 22:18:36 EDT 2007
+#Thu Sep 30 14:58:10 CEST 2010
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_core
formatter_settings_version=11
org.eclipse.jdt.ui.ignorelowercasenames=true
org.eclipse.jdt.ui.importorder=;
@@ -11,8 +10,10 @@ sp_cleanup.add_default_serial_version_id=true
sp_cleanup.add_generated_serial_version_id=false
sp_cleanup.add_missing_annotations=true
sp_cleanup.add_missing_deprecated_annotations=true
+sp_cleanup.add_missing_methods=false
sp_cleanup.add_missing_nls_tags=false
sp_cleanup.add_missing_override_annotations=true
+sp_cleanup.add_missing_override_annotations_interface_methods=false
sp_cleanup.add_serial_version_id=false
sp_cleanup.always_use_blocks=true
sp_cleanup.always_use_parentheses_in_expressions=false
@@ -20,15 +21,17 @@ sp_cleanup.always_use_this_for_non_static_field_access=false
sp_cleanup.always_use_this_for_non_static_method_access=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.correct_indentation=false
-sp_cleanup.format_source_code=true
+sp_cleanup.format_source_code=false
+sp_cleanup.format_source_code_changes_only=false
sp_cleanup.make_local_variable_final=false
sp_cleanup.make_parameters_final=false
sp_cleanup.make_private_fields_final=true
+sp_cleanup.make_type_abstract_if_missing_method=false
sp_cleanup.make_variable_declarations_final=true
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.on_save_use_additional_actions=false
-sp_cleanup.organize_imports=true
+sp_cleanup.organize_imports=false
sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.ltk.core.refactoring.prefs b/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.ltk.core.refactoring.prefs
new file mode 100644
index 000000000..91d4e29bd
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/.settings/org.eclipse.ltk.core.refactoring.prefs
@@ -0,0 +1,3 @@
+#Thu Sep 30 14:58:10 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/META-INF/MANIFEST.MF b/providers/bundles/org.eclipse.ecf.provider.jmdns/META-INF/MANIFEST.MF
index 2911b0135..5a1fe492d 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/META-INF/MANIFEST.MF
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.ecf.provider.jmdns;singleton:=true
-Bundle-Version: 4.0.100.qualifier
+Bundle-Version: 4.1.0.qualifier
Bundle-Activator: org.eclipse.ecf.internal.provider.jmdns.JMDNSPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
@@ -11,12 +11,16 @@ Require-Bundle: org.eclipse.equinox.common,
org.eclipse.ecf.discovery
Eclipse-LazyStart: true
Bundle-ClassPath: .
-Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,
- J2SE-1.4
+Bundle-RequiredExecutionEnvironment: J2SE-1.5,
+ J2SE-1.4,
+ CDC-1.1/Foundation-1.1
Export-Package: org.eclipse.ecf.internal.provider.jmdns;x-internal:=true,
org.eclipse.ecf.provider.jmdns.container,
org.eclipse.ecf.provider.jmdns.identity
-Import-Package: org.osgi.framework;version="1.3.0",
+Import-Package: edu.emory.mathcs.backport.java.util.concurrent;version="3.1.0",
+ edu.emory.mathcs.backport.java.util.concurrent.locks;version="3.1.0",
+ org.eclipse.equinox.concurrent.future;version="1.0.0",
+ org.osgi.framework;version="1.3.0",
org.osgi.service.log;version="1.3.0",
org.osgi.util.tracker;version="1.3.2"
Bundle-ActivationPolicy: lazy
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/build.properties b/providers/bundles/org.eclipse.ecf.provider.jmdns/build.properties
index 6857d101a..577d72967 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/build.properties
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/build.properties
@@ -11,5 +11,6 @@ src.includes = about.html,\
source.. = src/,\
jmdns/
output.. = bin/
-jre.compilation.profile = J2SE-1.4
+javacSource = 1.5
+javacTarget = 1.4
javacErrors.. = -assertIdentifier,-enumIdentifier
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/JmDNS.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/JmDNS.java
index e0b6c822e..90a979a50 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/JmDNS.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/JmDNS.java
@@ -2,11 +2,11 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
package javax.jmdns;
import java.io.IOException;
import java.net.InetAddress;
+import java.util.Map;
import javax.jmdns.impl.JmDNSImpl;
@@ -14,134 +14,252 @@ import javax.jmdns.impl.JmDNSImpl;
* mDNS implementation in Java.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Rick Blair, Jeff Sonstein,
- * Werner Randelshofer, Pierre Frisch, Scott Lewis, Scott Cytacki
+ * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Scott Lewis, Scott Cytacki
*/
public abstract class JmDNS
{
/**
* The version of JmDNS.
*/
- public static String VERSION = "2.0";
+ public static final String VERSION = "3.2.2";
/**
* Create an instance of JmDNS.
+ *
+ * @return jmDNS instance
+ * @throws IOException
*/
public static JmDNS create() throws IOException
{
- return new JmDNSImpl();
+ return new JmDNSImpl(null, null);
}
-
+
/**
- * Create an instance of JmDNS and bind it to a
- * specific network interface given its IP-address.
+ * Create an instance of JmDNS and bind it to a specific network interface given its IP-address.
+ *
+ * @param addr
+ * IP address to bind to.
+ * @return jmDNS instance
+ * @throws IOException
*/
public static JmDNS create(InetAddress addr) throws IOException
{
- return new JmDNSImpl(addr);
+ return new JmDNSImpl(addr, null);
+ }
+
+ /**
+ * Create an instance of JmDNS.
+ *
+ * @param name
+ * name of the newly created JmDNS
+ * @return jmDNS instance
+ * @throws IOException
+ */
+ public static JmDNS create(String name) throws IOException
+ {
+ return new JmDNSImpl(null, name);
}
-
+
/**
- * Return the HostName associated with this JmDNS instance.
- * Note: May not be the same as what started. The host name is subject to
- * negotiation.
+ * Create an instance of JmDNS and bind it to a specific network interface given its IP-address.
+ *
+ * @param addr
+ * IP address to bind to.
+ * @param name
+ * name of the newly created JmDNS
+ * @return jmDNS instance
+ * @throws IOException
+ */
+ public static JmDNS create(InetAddress addr, String name) throws IOException
+ {
+ return new JmDNSImpl(addr, name);
+ }
+
+ /**
+ * Return the name of the JmDNS instance. This is an arbitrary string that is useful for distinguishing instances.
+ *
+ * @return name of the JmDNS
+ */
+ public abstract String getName();
+
+ /**
+ * Return the HostName associated with this JmDNS instance. Note: May not be the same as what started. The host name is subject to negotiation.
+ *
+ * @return Host name
*/
public abstract String getHostName();
/**
- * Return the address of the interface to which this instance of JmDNS is
- * bound.
+ * Return the address of the interface to which this instance of JmDNS is bound.
+ *
+ * @return Internet Address
+ * @throws IOException
*/
public abstract InetAddress getInterface() throws IOException;
/**
- * Get service information. If the information is not cached, the method
- * will block until updated information is received.
+ * Get service information. If the information is not cached, the method will block until updated information is received.
* <p/>
- * Usage note: Do not call this method from the AWT event dispatcher thread.
- * You will make the user interface unresponsive.
+ * Usage note: Do not call this method from the AWT event dispatcher thread. You will make the user interface unresponsive.
*
- * @param type fully qualified service type, such as <code>_http._tcp.local.</code> .
- * @param name unqualified service name, such as <code>foobar</code> .
+ * @param type
+ * fully qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
* @return null if the service information cannot be obtained
*/
public abstract ServiceInfo getServiceInfo(String type, String name);
/**
- * Get service information. If the information is not cached, the method
- * will block for the given timeout until updated information is received.
+ * Get service information. If the information is not cached, the method will block for the given timeout until updated information is received.
+ * <p/>
+ * Usage note: If you call this method from the AWT event dispatcher thread, use a small timeout, or you will make the user interface unresponsive.
+ *
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
+ * @param timeout
+ * timeout in milliseconds. Typical timeout should be 5s.
+ * @return null if the service information cannot be obtained
+ */
+ public abstract ServiceInfo getServiceInfo(String type, String name, long timeout);
+
+ /**
+ * Get service information. If the information is not cached, the method will block until updated information is received.
+ * <p/>
+ * Usage note: Do not call this method from the AWT event dispatcher thread. You will make the user interface unresponsive.
+ *
+ * @param type
+ * fully qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @return null if the service information cannot be obtained
+ */
+ public abstract ServiceInfo getServiceInfo(String type, String name, boolean persistent);
+
+ /**
+ * Get service information. If the information is not cached, the method will block for the given timeout until updated information is received.
* <p/>
- * Usage note: If you call this method from the AWT event dispatcher thread,
- * use a small timeout, or you will make the user interface unresponsive.
+ * Usage note: If you call this method from the AWT event dispatcher thread, use a small timeout, or you will make the user interface unresponsive.
*
- * @param type full qualified service type, such as <code>_http._tcp.local.</code> .
- * @param name unqualified service name, such as <code>foobar</code> .
- * @param timeout timeout in milliseconds
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
+ * @param timeout
+ * timeout in milliseconds. Typical timeout should be 5s.
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
* @return null if the service information cannot be obtained
*/
- public abstract ServiceInfo getServiceInfo(String type, String name, int timeout);
+ public abstract ServiceInfo getServiceInfo(String type, String name, boolean persistent, long timeout);
/**
- * Request service information. The information about the service is
- * requested and the ServiceListener.resolveService method is called as soon
- * as it is available.
+ * Request service information. The information about the service is requested and the ServiceListener.resolveService method is called as soon as it is available.
* <p/>
- * Usage note: Do not call this method from the AWT event dispatcher thread.
- * You will make the user interface unresponsive.
+ * Usage note: Do not call this method from the AWT event dispatcher thread. You will make the user interface unresponsive.
*
- * @param type full qualified service type, such as <code>_http._tcp.local.</code> .
- * @param name unqualified service name, such as <code>foobar</code> .
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
*/
public abstract void requestServiceInfo(String type, String name);
-
+
+ /**
+ * Request service information. The information about the service is requested and the ServiceListener.resolveService method is called as soon as it is available.
+ * <p/>
+ * Usage note: Do not call this method from the AWT event dispatcher thread. You will make the user interface unresponsive.
+ *
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ */
+ public abstract void requestServiceInfo(String type, String name, boolean persistent);
+
+ /**
+ * Request service information. The information about the service is requested and the ServiceListener.resolveService method is called as soon as it is available.
+ *
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
+ * @param timeout
+ * timeout in milliseconds
+ */
+ public abstract void requestServiceInfo(String type, String name, long timeout);
+
/**
- * Request service information. The information about the service is requested
- * and the ServiceListener.resolveService method is called as soon as it is available.
+ * Request service information. The information about the service is requested and the ServiceListener.resolveService method is called as soon as it is available.
*
- * @param type full qualified service type, such as <code>_http._tcp.local.</code> .
- * @param name unqualified service name, such as <code>foobar</code> .
- * @param timeout timeout in milliseconds
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code> .
+ * @param name
+ * unqualified service name, such as <code>foobar</code> .
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param timeout
+ * timeout in milliseconds
*/
- public abstract void requestServiceInfo(String type, String name, int timeout);
+ public abstract void requestServiceInfo(String type, String name, boolean persistent, long timeout);
/**
* Listen for service types.
*
- * @param listener listener for service types
+ * @param listener
+ * listener for service types
+ * @throws IOException
*/
public abstract void addServiceTypeListener(ServiceTypeListener listener) throws IOException;
/**
* Remove listener for service types.
*
- * @param listener listener for service types
+ * @param listener
+ * listener for service types
*/
public abstract void removeServiceTypeListener(ServiceTypeListener listener);
/**
- * Listen for services of a given type. The type has to be a fully qualified
- * type name such as <code>_http._tcp.local.</code>.
+ * Listen for services of a given type. The type has to be a fully qualified type name such as <code>_http._tcp.local.</code>.
*
- * @param type full qualified service type, such as <code>_http._tcp.local.</code>.
- * @param listener listener for service updates
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code>.
+ * @param listener
+ * listener for service updates
*/
public abstract void addServiceListener(String type, ServiceListener listener);
/**
* Remove listener for services of a given type.
*
- * @param listener listener for service updates
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code>.
+ * @param listener
+ * listener for service updates
*/
public abstract void removeServiceListener(String type, ServiceListener listener);
/**
- * Register a service. The service is registered for access by other jmdns clients.
- * The name of the service may be changed to make it unique.
+ * Register a service. The service is registered for access by other jmdns clients. The name of the service may be changed to make it unique.
+ *
+ * @param info
+ * service info to register
+ * @throws IOException
*/
public abstract void registerService(ServiceInfo info) throws IOException;
/**
* Unregister a service. The service should have been registered.
+ *
+ * @param info
+ * service info to remove
*/
public abstract void unregisterService(ServiceInfo info);
@@ -151,11 +269,13 @@ public abstract class JmDNS
public abstract void unregisterAllServices();
/**
- * Register a service type. If this service type was not already known,
- * all service listeners will be notified of the new service type. Service types
- * are automatically registered as they are discovered.
+ * Register a service type. If this service type was not already known, all service listeners will be notified of the new service type. Service types are automatically registered as they are discovered.
+ *
+ * @param type
+ * full qualified service type, such as <code>_http._tcp.local.</code>.
+ * @return <code>true</code> if the type or subtype was added, <code>false</code> if the type was already registered.
*/
- public abstract void registerServiceType(String type);
+ public abstract boolean registerServiceType(String type);
/**
* Close down jmdns. Release all resources and unregister all services.
@@ -163,17 +283,48 @@ public abstract class JmDNS
public abstract void close();
/**
- * List Services and serviceTypes.
- * Debugging Only
+ * List Services and serviceTypes. Debugging Only
*/
public abstract void printServices();
/**
* Returns a list of service infos of the specified type.
*
- * @param type Service type name, such as <code>_http._tcp.local.</code>.
- * @return An array of service instance names.
+ * @param type
+ * Service type name, such as <code>_http._tcp.local.</code>.
+ * @return An array of service instance.
*/
public abstract ServiceInfo[] list(String type);
+ /**
+ * Returns a list of service infos of the specified type.
+ *
+ * @param type
+ * Service type name, such as <code>_http._tcp.local.</code>.
+ * @param timeout
+ * timeout in milliseconds. Typical timeout should be 6s.
+ * @return An array of service instance.
+ */
+ public abstract ServiceInfo[] list(String type, long timeout);
+
+ /**
+ * Returns a list of service infos of the specified type sorted by subtype. Any service that do not register a subtype is listed in the empty subtype section.
+ *
+ * @param type
+ * Service type name, such as <code>_http._tcp.local.</code>.
+ * @return A dictionary of service info by subtypes.
+ */
+ public abstract Map<String, ServiceInfo[]> listBySubtype(String type);
+
+ /**
+ * Returns a list of service infos of the specified type sorted by subtype. Any service that do not register a subtype is listed in the empty subtype section.
+ *
+ * @param type
+ * Service type name, such as <code>_http._tcp.local.</code>.
+ * @param timeout
+ * timeout in milliseconds. Typical timeout should be 6s.
+ * @return A dictionary of service info by subtypes.
+ */
+ public abstract Map<String, ServiceInfo[]> listBySubtype(String type, long timeout);
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceEvent.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceEvent.java
index 3d3807efb..aa2d1b695 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceEvent.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceEvent.java
@@ -6,38 +6,62 @@ package javax.jmdns;
import java.util.EventObject;
+/**
+ *
+ */
public abstract class ServiceEvent extends EventObject
{
- public ServiceEvent(Object source)
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8558445644541006271L;
+
+ /**
+ * Constructs a Service Event.
+ *
+ * @param eventSource
+ * The object on which the Event initially occurred.
+ * @exception IllegalArgumentException
+ * if source is null.
+ */
+ public ServiceEvent(Object eventSource)
{
- super(source);
- // TODO Auto-generated constructor stub
+ super(eventSource);
}
/**
* Returns the JmDNS instance which originated the event.
+ *
+ * @return JmDNS instance
*/
public abstract JmDNS getDNS();
/**
* Returns the fully qualified type of the service.
+ *
+ * @return type of the service.
*/
public abstract String getType();
/**
- * Returns the instance name of the service.
- * Always returns null, if the event is sent to a service type listener.
+ * Returns the instance name of the service. Always returns null, if the event is sent to a service type listener.
+ *
+ * @return name of the service
*/
public abstract String getName();
/**
- * Returns the service info record, or null if the service could not be
- * resolved.
- * Always returns null, if the event is sent to a service type listener.
- */
- /**
+ * Returns the service info record, or null if the service could not be resolved. Always returns null, if the event is sent to a service type listener.
+ *
+ * @return service info record
* @see javax.jmdns.ServiceEvent#getInfo()
*/
public abstract ServiceInfo getInfo();
+
+ @Override
+ public Object clone() throws CloneNotSupportedException
+ {
+ return super.clone();
+ }
} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceInfo.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceInfo.java
index 62de76f73..979cc93b7 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceInfo.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceInfo.java
@@ -3,169 +3,638 @@
//Original license LGPL
package javax.jmdns;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Enumeration;
-import java.util.Hashtable;
+import java.util.Map;
import javax.jmdns.impl.ServiceInfoImpl;
+/**
+ * <p>
+ * The fully qualified service name is build using up to 5 components with the following structure:
+ *
+ * <pre>
+ * &lt;app&gt;.&lt;protocol&gt;.&lt;servicedomain&gt;.&lt;parentdomain&gt;.<br/>
+ * &lt;Instance&gt;.&lt;app&gt;.&lt;protocol&gt;.&lt;servicedomain&gt;.&lt;parentdomain&gt;.<br/>
+ * &lt;sub&gt;._sub.&lt;app&gt;.&lt;protocol&gt;.&lt;servicedomain&gt;.&lt;parentdomain&gt;.
+ * </pre>
+ *
+ * <ol>
+ * <li>&lt;servicedomain&gt;.&lt;parentdomain&gt;: This is the domain scope of the service typically "local.", but this can also be something similar to "in-addr.arpa." or "ip6.arpa."</li>
+ * <li>&lt;protocol&gt;: This is either "_tcp" or "_udp"</li>
+ * <li>&lt;app&gt;: This define the application protocol. Typical example are "_http", "_ftp", etc.</li>
+ * <li>&lt;Instance&gt;: This is the service name</li>
+ * <li>&lt;sub&gt;: This is the subtype for the application protocol</li>
+ * </ol>
+ * </p>
+ */
public abstract class ServiceInfo
{
- public final static byte[] NO_VALUE = new byte[0];
+ public static final byte[] NO_VALUE = new byte[0];
+
+ public enum Fields
+ {
+ Domain, Protocol, Application, Instance, Subtype
+ }
/**
- * Construct a service description for registrating with JmDNS.
+ * Construct a service description for registering with JmDNS.
*
- * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>.
- * @param name unqualified service instance name, such as <code>foobar</code>
- * @param port the local port on which the service runs
- * @param text string describing the service
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param text
+ * string describing the service
+ * @return new service info
*/
public static ServiceInfo create(String type, String name, int port, String text)
{
- return new ServiceInfoImpl(type, name, port, text);
+ return new ServiceInfoImpl(type, name, "", port, 0, 0, false, text);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param text
+ * string describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, String subtype, int port, String text)
+ {
+ return new ServiceInfoImpl(type, name, subtype, port, 0, 0, false, text);
}
/**
- * Construct a service description for registrating with JmDNS.
+ * Construct a service description for registering with JmDNS.
*
- * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>.
- * @param name unqualified service instance name, such as <code>foobar</code>
- * @param port the local port on which the service runs
- * @param weight weight of the service
- * @param priority priority of the service
- * @param text string describing the service
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param text
+ * string describing the service
+ * @return new service info
*/
public static ServiceInfo create(String type, String name, int port, int weight, int priority, String text)
{
- return new ServiceInfoImpl(type, name, port, weight, priority, text);
+ return new ServiceInfoImpl(type, name, "", port, weight, priority, false, text);
}
/**
- * Construct a service description for registrating with JmDNS. The properties hashtable must
- * map property names to either Strings or byte arrays describing the property values.
+ * Construct a service description for registering with JmDNS.
*
- * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>.
- * @param name unqualified service instance name, such as <code>foobar</code>
- * @param port the local port on which the service runs
- * @param weight weight of the service
- * @param priority priority of the service
- * @param props properties describing the service
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param text
+ * string describing the service
+ * @return new service info
*/
- public static ServiceInfo create(String type, String name, int port, int weight, int priority, Hashtable props)
+ public static ServiceInfo create(String type, String name, String subtype, int port, int weight, int priority, String text)
{
- return new ServiceInfoImpl(type, name, port, weight, priority, props);
+ return new ServiceInfoImpl(type, name, subtype, port, weight, priority, false, text);
}
/**
- * Construct a service description for registrating with JmDNS.
+ * Construct a service description for registering with JmDNS. The properties hashtable must map property names to either Strings or byte arrays describing the property values.
*
- * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>.
- * @param name unqualified service instance name, such as <code>foobar</code>
- * @param port the local port on which the service runs
- * @param weight weight of the service
- * @param priority priority of the service
- * @param text bytes describing the service
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param props
+ * properties describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, int port, int weight, int priority, Map<String, ?> props)
+ {
+ return new ServiceInfoImpl(type, name, "", port, weight, priority, false, props);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS. The properties hashtable must map property names to either Strings or byte arrays describing the property values.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param props
+ * properties describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, String subtype, int port, int weight, int priority, Map<String, ?> props)
+ {
+ return new ServiceInfoImpl(type, name, subtype, port, weight, priority, false, props);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param text
+ * bytes describing the service
+ * @return new service info
*/
public static ServiceInfo create(String type, String name, int port, int weight, int priority, byte text[])
{
- return new ServiceInfoImpl(type, name, port, weight, priority, text);
+ return new ServiceInfoImpl(type, name, "", port, weight, priority, false, text);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param text
+ * bytes describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, String subtype, int port, int weight, int priority, byte text[])
+ {
+ return new ServiceInfoImpl(type, name, subtype, port, weight, priority, false, text);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param text
+ * string describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, int port, int weight, int priority, boolean persistent, String text)
+ {
+ return new ServiceInfoImpl(type, name, "", port, weight, priority, persistent, text);
}
/**
- * Fully qualified service type name, such as <code>_http._tcp.local.</code> .
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param text
+ * string describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, String text)
+ {
+ return new ServiceInfoImpl(type, name, subtype, port, weight, priority, persistent, text);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS. The properties hashtable must map property names to either Strings or byte arrays describing the property values.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param props
+ * properties describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, int port, int weight, int priority, boolean persistent, Map<String, ?> props)
+ {
+ return new ServiceInfoImpl(type, name, "", port, weight, priority, persistent, props);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS. The properties hashtable must map property names to either Strings or byte arrays describing the property values.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param props
+ * properties describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, Map<String, ?> props)
+ {
+ return new ServiceInfoImpl(type, name, subtype, port, weight, priority, persistent, props);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param text
+ * bytes describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, int port, int weight, int priority, boolean persistent, byte text[])
+ {
+ return new ServiceInfoImpl(type, name, "", port, weight, priority, persistent, text);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS.
+ *
+ * @param type
+ * fully qualified service type name, such as <code>_http._tcp.local.</code>.
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
+ * @param subtype
+ * service subtype see draft-cheshire-dnsext-dns-sd-06.txt chapter 7.1 Selective Instance Enumeration
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param text
+ * bytes describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, byte text[])
+ {
+ return new ServiceInfoImpl(type, name, subtype, port, weight, priority, persistent, text);
+ }
+
+ /**
+ * Construct a service description for registering with JmDNS. The properties hashtable must map property names to either Strings or byte arrays describing the property values.
+ *
+ * @param qualifiedNameMap
+ * dictionary of values to build the fully qualified service name. Mandatory keys are Application and Instance. The Domain default is local, the Protocol default is tcp and the subtype default is none.
+ * @param port
+ * the local port on which the service runs
+ * @param weight
+ * weight of the service
+ * @param priority
+ * priority of the service
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @param props
+ * properties describing the service
+ * @return new service info
+ */
+ public static ServiceInfo create(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, Map<String, ?> props)
+ {
+ return new ServiceInfoImpl(qualifiedNameMap, port, weight, priority, persistent, props);
+ }
+
+ /**
+ * Returns true if the service info is filled with data.
+ *
+ * @return <code>true</code> if the service info has data, <code>false</code> otherwise.
+ */
+ public abstract boolean hasData();
+
+ /**
+ * Fully qualified service type name, such as <code>_http._tcp.local.</code>
+ *
+ * @return service type name
*/
public abstract String getType();
/**
+ * Fully qualified service type name with the subtype if appropriate, such as <code>_printer._sub._http._tcp.local.</code>
+ *
+ * @return service type name
+ */
+ public abstract String getTypeWithSubtype();
+
+ /**
* Unqualified service instance name, such as <code>foobar</code> .
+ *
+ * @return service name
*/
public abstract String getName();
/**
* Fully qualified service name, such as <code>foobar._http._tcp.local.</code> .
+ *
+ * @return qualified service name
*/
public abstract String getQualifiedName();
/**
* Get the name of the server.
+ *
+ * @return server name
*/
public abstract String getServer();
/**
- * Get the host address of the service (ie X.X.X.X).
+ * Returns the host IP address string in textual presentation.<br/>
+ * <b>Note:</b> This can be either an IPv4 or an IPv6 representation.
+ *
+ * @return the host raw IP address in a string format.
*/
public abstract String getHostAddress();
+ /**
+ * Get the host address of the service.<br/>
+ *
+ *
+ * @return host Internet address
+ * @deprecated since 3.1.8
+ * @see #getInetAddress()
+ */
+ @Deprecated
public abstract InetAddress getAddress();
/**
- * Get the InetAddress of the service.
+ * Get the InetAddress of the service. This will return the IPv4 if it exist, otherwise it return the IPv6 if set.<br/>
+ * <b>Note:</b> This return null if the service IP address cannot be resolved.
+ *
+ * @return Internet address
*/
public abstract InetAddress getInetAddress();
/**
+ * Get the IPv4 InetAddress of the service.<br/>
+ * <b>Note:</b> This return null if the service IPv4 address cannot be resolved.
+ *
+ * @return Internet address
+ */
+ public abstract Inet4Address getInet4Address();
+
+ /**
+ * Get the IPv6 InetAddress of the service.<br/>
+ * <b>Note:</b> This return null if the service IPv6 address cannot be resolved.
+ *
+ * @return Internet address
+ */
+ public abstract Inet6Address getInet6Address();
+
+ /**
* Get the port for the service.
+ *
+ * @return service port
*/
public abstract int getPort();
/**
* Get the priority of the service.
+ *
+ * @return service priority
*/
public abstract int getPriority();
/**
* Get the weight of the service.
+ *
+ * @return service weight
*/
public abstract int getWeight();
/**
- * Get the text for the serivce as raw bytes.
+ * Get the text for the service as raw bytes.
+ *
+ * @return raw service text
*/
public abstract byte[] getTextBytes();
/**
- * Get the text for the service. This will interpret the text bytes
- * as a UTF8 encoded string. Will return null if the bytes are not
- * a valid UTF8 encoded string.
+ * Get the text for the service. This will interpret the text bytes as a UTF8 encoded string. Will return null if the bytes are not a valid UTF8 encoded string.<br/>
+ * <b>Note:</b> Do not use. This method make the assumption that the TXT record is one string. This is false. The TXT record is a series of key value pairs.
+ *
+ * @return service text
+ * @see #getPropertyNames()
+ * @see #getPropertyBytes(String)
+ * @see #getPropertyString(String)
+ * @deprecated since 3.1.7
*/
+ @Deprecated
public abstract String getTextString();
/**
- * Get the URL for this service. An http URL is created by
- * combining the address, port, and path properties.
+ * Get the URL for this service. An http URL is created by combining the address, port, and path properties.
+ *
+ * @return service URL
*/
public abstract String getURL();
/**
- * Get the URL for this service. An URL is created by
- * combining the protocol, address, port, and path properties.
+ * Get the URL for this service. An URL is created by combining the protocol, address, port, and path properties.
+ *
+ * @param protocol
+ * requested protocol
+ * @return service URL
*/
public abstract String getURL(String protocol);
/**
- * Get a property of the service. This involves decoding the
- * text bytes into a property list. Returns null if the property
- * is not found or the text data could not be decoded correctly.
+ * Get a property of the service. This involves decoding the text bytes into a property list. Returns null if the property is not found or the text data could not be decoded correctly.
+ *
+ * @param name
+ * property name
+ * @return raw property text
*/
public abstract byte[] getPropertyBytes(String name);
/**
- * Get a property of the service. This involves decoding the
- * text bytes into a property list. Returns null if the property
- * is not found, the text data could not be decoded correctly, or
- * the resulting bytes are not a valid UTF8 string.
+ * Get a property of the service. This involves decoding the text bytes into a property list. Returns null if the property is not found, the text data could not be decoded correctly, or the resulting bytes are not a valid UTF8 string.
+ *
+ * @param name
+ * property name
+ * @return property text
*/
public abstract String getPropertyString(String name);
/**
* Enumeration of the property names.
+ *
+ * @return property name enumeration
*/
- public abstract Enumeration getPropertyNames();
+ public abstract Enumeration<String> getPropertyNames();
+ /**
+ * Returns a description of the service info suitable for printing.
+ *
+ * @return service info description
+ */
public abstract String getNiceTextString();
-
- public abstract String getProtocol();
+ /**
+ * Set the text for the service. Setting the text will fore a re-announce of the service.
+ *
+ * @param text
+ * the raw byte representation of the text field.
+ * @throws IllegalStateException
+ * if attempting to set the text for a non persistent service info.
+ */
+ public abstract void setText(byte[] text) throws IllegalStateException;
+
+ /**
+ * Set the text for the service. Setting the text will fore a re-announce of the service.
+ *
+ * @param props
+ * a key=value map that will be encoded into raw bytes.
+ * @throws IllegalStateException
+ * if attempting to set the text for a non persistent service info.
+ */
+ public abstract void setText(Map<String, ?> props) throws IllegalStateException;
+
+ /**
+ * Returns <code>true</code> if ServiceListener.resolveService will be called whenever new new information is received.
+ *
+ * @return the persistent
+ */
+ public abstract boolean isPersistent();
+
+ /**
+ * Returns the domain of the service info suitable for printing.
+ *
+ * @return service domain
+ */
public abstract String getDomain();
+
+ /**
+ * Returns the protocol of the service info suitable for printing.
+ *
+ * @return service protocol
+ */
+ public abstract String getProtocol();
+
+ /**
+ * Returns the application of the service info suitable for printing.
+ *
+ * @return service application
+ */
+ public abstract String getApplication();
+
+ /**
+ * Returns the sub type of the service info suitable for printing.
+ *
+ * @return service sub type
+ */
+ public abstract String getSubtype();
+
+ /**
+ * Returns a dictionary of the fully qualified name component of this service.
+ *
+ * @return dictionary of the fully qualified name components
+ */
+ public abstract Map<Fields, String> getQualifiedNameMap();
+
+ @Override
+ public Object clone()
+ {
+ try
+ {
+ return super.clone();
+ }
+ catch (CloneNotSupportedException exception)
+ {
+ // clone is supported
+ return null;
+ }
+ }
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceListener.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceListener.java
index 264aee730..17009cea3 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceListener.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceListener.java
@@ -10,33 +10,41 @@ import java.util.EventListener;
* Listener for service updates.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Werner Randelshofer
+ * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch
*/
public interface ServiceListener extends EventListener
{
/**
- * A service has been added.
+ * A service has been added.<br/>
+ * <b>Note:</b>This event is only the service added event. The service info associated with this event does not include resolution information.<br/>
+ * To get the full resolved information you need to listen to {@link #serviceResolved(ServiceEvent)} or call {@link JmDNS#getServiceInfo(String, String, long)}
*
- * @param event The ServiceEvent providing the name and fully qualified type
- * of the service.
+ * <pre>
+ * ServiceInfo info = event.getDNS().getServiceInfo(event.getType(), event.getName())
+ * </pre>
+ * <p>
+ * Please note that service resolution may take a few second to resolve.
+ * </p>
+ *
+ * @param event
+ * The ServiceEvent providing the name and fully qualified type of the service.
*/
void serviceAdded(ServiceEvent event);
/**
* A service has been removed.
*
- * @param event The ServiceEvent providing the name and fully qualified type
- * of the service.
+ * @param event
+ * The ServiceEvent providing the name and fully qualified type of the service.
*/
void serviceRemoved(ServiceEvent event);
/**
- * A service has been resolved. Its details are now available in the
- * ServiceInfo record.
+ * A service has been resolved. Its details are now available in the ServiceInfo record.<br/>
+ * <b>Note:</b>This call back will never be called if the service does not resolve.<br/>
*
- * @param event The ServiceEvent providing the name, the fully qualified
- * type of the service, and the service info record, or null if the service
- * could not be resolved.
+ * @param event
+ * The ServiceEvent providing the name, the fully qualified type of the service, and the service info record.
*/
void serviceResolved(ServiceEvent event);
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceTypeListener.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceTypeListener.java
index a3d1c835f..3a8c1cac0 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceTypeListener.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/ServiceTypeListener.java
@@ -10,15 +10,28 @@ import java.util.EventListener;
* Listener for service types.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Werner Randelshofer
+ * @author Arthur van Hoff, Werner Randelshofer
*/
public interface ServiceTypeListener extends EventListener
{
/**
* A new service type was discovered.
*
- * @param event The service event providing the fully qualified type of
- * the service.
+ * @param event
+ * The service event providing the fully qualified type of the service.
*/
void serviceTypeAdded(ServiceEvent event);
+
+ /**
+ * A new subtype for the service type was discovered.
+ *
+ * <pre>
+ * &lt;sub&gt;._sub.&lt;app&gt;.&lt;protocol&gt;.&lt;servicedomain&gt;.&lt;parentdomain&gt;.
+ * </pre>
+ *
+ * @param event
+ * The service event providing the fully qualified type of the service with subtype.
+ * @since 3.2.0
+ */
+ void subTypeForServiceTypeAdded(ServiceEvent event);
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSCache.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSCache.java
index 9446c1623..5716712d8 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSCache.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSCache.java
@@ -4,247 +4,603 @@
package javax.jmdns.impl;
+import java.util.AbstractMap;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-//import java.util.logging.Logger;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
/**
- * A table of DNS entries. This is a hash table which can handle multiple
- * entries with the same name. <p/> Storing multiple entries with the same name
- * is implemented using a linked list of <code>CacheNode</code>'s. <p/> The
- * current implementation of the API of DNSCache does expose the cache nodes to
- * clients. Clients must explicitly deal with the nodes when iterating over
- * entries in the cache. Here's how to iterate over all entries in the cache:
- *
+ * A table of DNS entries. This is a map table which can handle multiple entries with the same name.
+ * <p/>
+ * Storing multiple entries with the same name is implemented using a linked list. This is hidden from the user and can change in later implementation.
+ * <p/>
+ * Here's how to iterate over all entries:
+ *
* <pre>
- * for (Iterator i=dnscache.iterator(); i.hasNext(); ) {
- * for (DNSCache.CacheNode n = (DNSCache.CacheNode) i.next(); n != null; n.next()) {
- * DNSEntry entry = n.getValue();
- * ...do something with entry...
- * }
- * }
+ * for (Iterator i=dnscache.allValues().iterator(); i.hasNext(); ) {
+ * DNSEntry entry = i.next();
+ * ...do something with entry...
+ * }
* </pre>
- *
- * <p/> And here's how to iterate over all entries having a given name:
- *
+ *
+ * <p/>
+ * And here's how to iterate over all entries having a given name:
+ *
* <pre>
- * for (DNSCache.CacheNode n = (DNSCache.CacheNode) dnscache.find(name); n != null; n.next()) {
- * DNSEntry entry = n.getValue();
- * ...do something with entry...
- * }
+ * for (Iterator i=dnscache.getDNSEntryList(name).iterator(); i.hasNext(); ) {
+ * DNSEntry entry = i.next();
+ * ...do something with entry...
+ * }
* </pre>
- *
+ *
* @version %I%, %G%
- * @author Arthur van Hoff, Werner Randelshofer, Rick Blair
+ * @author Arthur van Hoff, Werner Randelshofer, Rick Blair, Pierre Frisch
*/
-public class DNSCache
+public class DNSCache extends AbstractMap<String, List<? extends DNSEntry>>
{
-// private static Logger logger = Logger.getLogger(DNSCache.class.getName());
- // Implementation note:
- // We might completely hide the existence of CacheNode's in a future version
- // of DNSCache. But this will require to implement two (inner) classes for
- // the iterators that will be returned by method <code>iterator()</code> and
- // method <code>find(name)</code>.
- // Since DNSCache is not a public class, it does not seem worth the effort
- // to clean its API up that much.
-
- // [PJYF Oct 15 2004] This should implements Collections that would be amuch
- // cleaner implementation
- /**
- * The number of DNSEntry's in the cache.
- */
- private int size;
+ // private static Logger logger = Logger.getLogger(DNSCache.class.getName());
- /**
- * The hashtable used internally to store the entries of the cache. Keys are
- * instances of String. The String contains an unqualified service name.
- * Values are linked lists of CacheNode instances.
- */
- private final HashMap hashtable;
+ private transient Set<Map.Entry<String, List<? extends DNSEntry>>> _entrySet = null;
/**
- * Cache nodes are used to implement storage of multiple DNSEntry's of the
- * same name in the cache.
+ *
*/
- public static class CacheNode
+ public static final DNSCache EmptyCache = new _EmptyCache();
+
+ static class _EmptyCache extends DNSCache
{
-// private static Logger logger = Logger.getLogger(CacheNode.class.getName());
- private final DNSEntry value;
- private CacheNode next;
- public CacheNode(DNSEntry value)
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#size()
+ */
+ @Override
+ public int size()
{
- this.value = value;
+ return 0;
}
- public CacheNode next()
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#isEmpty()
+ */
+ @Override
+ public boolean isEmpty()
{
- return next;
+ return true;
}
- public DNSEntry getValue()
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#containsKey(java.lang.Object)
+ */
+ @Override
+ public boolean containsKey(Object key)
{
- return value;
+ return false;
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#containsValue(java.lang.Object)
+ */
+ @Override
+ public boolean containsValue(Object value)
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#get(java.lang.Object)
+ */
+ @Override
+ public List<DNSEntry> get(Object key)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#keySet()
+ */
+ @Override
+ public Set<String> keySet()
+ {
+ return Collections.emptySet();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#values()
+ */
+ @Override
+ public Collection<List<? extends DNSEntry>> values()
+ {
+ return Collections.emptySet();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSCache#entrySet()
+ */
+ @Override
+ public Set<Map.Entry<String, List<? extends DNSEntry>>> entrySet()
+ {
+ return Collections.emptySet();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object o)
+ {
+ return (o instanceof Map) && ((Map<?, ?>) o).size() == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#put(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public List<? extends DNSEntry> put(String key, List<? extends DNSEntry> value)
+ {
+ return null;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return 0;
+ }
+
}
/**
- * Create a table with a given initial size.
+ *
*/
- public DNSCache(final int size)
+ protected static class _CacheEntry extends Object implements Map.Entry<String, List<? extends DNSEntry>>
{
- hashtable = new HashMap(size);
+
+ private List<? extends DNSEntry> _value;
+
+ private String _key;
+
+ /**
+ * @param key
+ * @param value
+ */
+ protected _CacheEntry(String key, List<? extends DNSEntry> value)
+ {
+ super();
+ _key = (key != null ? key.trim().toLowerCase() : null);
+ _value = value;
+ }
+
+ /**
+ * @param entry
+ */
+ protected _CacheEntry(Map.Entry<String, List<? extends DNSEntry>> entry)
+ {
+ super();
+ if (entry instanceof _CacheEntry)
+ {
+ _key = ((_CacheEntry) entry).getKey();
+ _value = ((_CacheEntry) entry).getValue();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.Map$Entry#getKey()
+ */
+ @Override
+ public String getKey()
+ {
+ return (_key != null ? _key : "");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.Map$Entry#getValue()
+ */
+ @Override
+ public List<? extends DNSEntry> getValue()
+ {
+ return _value;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.Map$Entry#setValue(java.lang.Object)
+ */
+ @Override
+ public List<? extends DNSEntry> setValue(List<? extends DNSEntry> value)
+ {
+ List<? extends DNSEntry> oldValue = _value;
+ _value = value;
+ return oldValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object entry)
+ {
+ if (!(entry instanceof Map.Entry))
+ return false;
+ return this.getKey().equals(((Map.Entry<?, ?>) entry).getKey()) && this.getValue().equals(((Map.Entry<?, ?>) entry).getValue());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return ((_key == null) ? 0 : _key.hashCode()) ^ ((_value == null) ? 0 : _value.hashCode());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public synchronized String toString()
+ {
+ StringBuffer aLog = new StringBuffer();
+ aLog.append("\n\t\tname '" + _key + "'");
+ if (_value != null)
+ {
+ for (DNSEntry entry : _value)
+ {
+ aLog.append("\n\t\t\t" + entry.toString());
+ }
+ }
+ else
+ {
+ aLog.append(" no entries");
+ }
+ return aLog.toString();
+ }
}
/**
- * Clears the cache.
+ *
*/
- public synchronized void clear()
+ public DNSCache()
{
- hashtable.clear();
- size = 0;
+ this(1024);
}
/**
- * Adds an entry to the table.
+ * @param map
*/
- public synchronized void add(final DNSEntry entry)
+ public DNSCache(DNSCache map)
{
- // logger.log("DNSCache.add("+entry.getName()+")");
- final CacheNode newValue = new CacheNode(entry);
- final CacheNode node = (CacheNode) hashtable.get(entry.getName());
- if (node == null)
+ this(map != null ? map.size() : 1024);
+ if (map != null)
{
- hashtable.put(entry.getName(), newValue);
+ this.putAll(map);
}
- else
- {
- newValue.next = node.next;
- node.next = newValue;
- }
- size++;
}
/**
- * Remove a specific entry from the table. Returns true if the entry was
- * found.
+ * Create a table with a given initial size.
+ *
+ * @param initialCapacity
*/
- public synchronized boolean remove(DNSEntry entry)
+ public DNSCache(int initialCapacity)
{
- CacheNode node = (CacheNode) hashtable.get(entry.getName());
- if (node != null)
+ super();
+ _entrySet = new HashSet<Map.Entry<String, List<? extends DNSEntry>>>(initialCapacity);
+ }
+
+ // ====================================================================
+ // Map
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#entrySet()
+ */
+ @Override
+ public Set<Map.Entry<String, List<? extends DNSEntry>>> entrySet()
+ {
+ if (_entrySet == null)
+ _entrySet = new HashSet<Map.Entry<String, List<? extends DNSEntry>>>();
+ return _entrySet;
+ }
+
+ /**
+ * @param key
+ * @return map entry for the key
+ */
+ protected Map.Entry<String, List<? extends DNSEntry>> getEntry(String key)
+ {
+ String stringKey = (key != null ? key.trim().toLowerCase() : null);
+ for (Map.Entry<String, List<? extends DNSEntry>> entry : this.entrySet())
{
- if (node.value == entry)
+ if (stringKey != null)
{
- if (node.next == null)
- {
- hashtable.remove(entry.getName());
- }
- else
- {
- hashtable.put(entry.getName(), node.next);
- }
- size--;
- return true;
+ if (stringKey.equals(entry.getKey()))
+ return entry;
}
+ else
+ {
+ if (entry.getKey() == null)
+ return entry;
+ }
+ }
+ return null;
+ }
- CacheNode previous = node;
- node = node.next;
- while (node != null)
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#put(java.lang.Object, java.lang.Object)
+ */
+ @Override
+ public List<? extends DNSEntry> put(String key, List<? extends DNSEntry> value)
+ {
+ synchronized (this)
+ {
+ List<? extends DNSEntry> oldValue = null;
+ Map.Entry<String, List<? extends DNSEntry>> oldEntry = this.getEntry(key);
+ if (oldEntry != null)
{
- if (node.value == entry)
- {
- previous.next = node.next;
- size--;
- return true;
- }
- previous = node;
- node = node.next;
+ oldValue = oldEntry.setValue(value);
+ }
+ else
+ {
+ this.entrySet().add(new _CacheEntry(key, value));
}
- ;
+ return oldValue;
}
- return false;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#clone()
+ */
+ @Override
+ protected Object clone() throws CloneNotSupportedException
+ {
+ return new DNSCache(this);
+ }
+
+ // ====================================================================
+
/**
- * Get a matching DNS entry from the table (using equals). Returns the entry
- * that was found.
+ * Returns all entries in the cache
+ *
+ * @return all entries in the cache
*/
- public synchronized DNSEntry get(DNSEntry entry)
+ public synchronized Collection<DNSEntry> allValues()
{
- for (CacheNode node = find(entry.getName()); node != null; node = node.next)
+ List<DNSEntry> allValues = new ArrayList<DNSEntry>();
+ for (List<? extends DNSEntry> entry : this.values())
{
- if (node.value.equals(entry))
+ if (entry != null)
+ allValues.addAll(entry);
+ }
+ return allValues;
+ }
+
+ /**
+ * Iterate only over items with matching name. Returns an list of DNSEntry or null. To retrieve all entries, one must iterate over this linked list.
+ *
+ * @param name
+ * @return list of DNSEntries
+ */
+ public synchronized Collection<? extends DNSEntry> getDNSEntryList(String name)
+ {
+ return this.get(name != null ? name.toLowerCase() : null);
+ }
+
+ /**
+ * Get a matching DNS entry from the table (using isSameEntry). Returns the entry that was found.
+ *
+ * @param dnsEntry
+ * @return DNSEntry
+ */
+ public synchronized DNSEntry getDNSEntry(DNSEntry dnsEntry)
+ {
+ DNSEntry result = null;
+ if (dnsEntry != null)
+ {
+ Collection<? extends DNSEntry> entryList = this.getDNSEntryList(dnsEntry.getKey());
+ if (entryList != null)
{
- return node.value;
+ for (DNSEntry testDNSEntry : entryList)
+ {
+ if (testDNSEntry.isSameEntry(dnsEntry))
+ {
+ result = testDNSEntry;
+ break;
+ }
+ }
}
}
- return null;
+ return result;
}
/**
* Get a matching DNS entry from the table.
+ *
+ * @param name
+ * @param type
+ * @param recordClass
+ * @return DNSEntry
*/
- public synchronized DNSEntry get(String name, int type, int clazz)
+ public synchronized DNSEntry getDNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass)
{
- for (CacheNode node = find(name); node != null; node = node.next)
+ DNSEntry result = null;
+ Collection<? extends DNSEntry> entryList = this.getDNSEntryList(name);
+ if (entryList != null)
{
- if (node.value.type == type && node.value.clazz == clazz)
+ for (DNSEntry testDNSEntry : entryList)
{
- return node.value;
+ if (testDNSEntry.getRecordType().equals(type) && ((DNSRecordClass.CLASS_ANY == recordClass) || testDNSEntry.getRecordClass().equals(recordClass)))
+ {
+ result = testDNSEntry;
+ break;
+ }
}
}
- return null;
+ return result;
}
/**
- * Iterates over all cache nodes. The iterator returns instances of
- * DNSCache.CacheNode. Each instance returned is the first node of a linked
- * list. To retrieve all entries, one must iterate over this linked list.
- * See code snippets in the header of the class.
+ * Adds an entry to the table.
+ *
+ * @param dnsEntry
+ * @return true if the entry was added
*/
- public synchronized Iterator iterator()
+ public synchronized boolean addDNSEntry(final DNSEntry dnsEntry)
{
- return new ArrayList(hashtable.values()).iterator();
+ boolean result = false;
+ if (dnsEntry != null)
+ {
+ Map.Entry<String, List<? extends DNSEntry>> oldEntry = this.getEntry(dnsEntry.getKey());
+
+ List<DNSEntry> aNewValue = null;
+ if (oldEntry != null)
+ {
+ aNewValue = new ArrayList<DNSEntry>(oldEntry.getValue());
+ }
+ else
+ {
+ aNewValue = new ArrayList<DNSEntry>();
+ }
+ aNewValue.add(dnsEntry);
+
+ if (oldEntry != null)
+ {
+ oldEntry.setValue(aNewValue);
+ }
+ else
+ {
+ this.entrySet().add(new _CacheEntry(dnsEntry.getKey(), aNewValue));
+ }
+ // This is probably not very informative
+ result = true;
+ }
+ return result;
}
/**
- * Iterate only over items with matching name. Returns an instance of
- * DNSCache.CacheNode or null. If an instance is returned, it is the first
- * node of a linked list. To retrieve all entries, one must iterate over
- * this linked list.
+ * Removes a specific entry from the table. Returns true if the entry was found.
+ *
+ * @param dnsEntry
+ * @return true if the entry was removed
*/
- public synchronized CacheNode find(String name)
+ public synchronized boolean removeDNSEntry(DNSEntry dnsEntry)
{
- return (CacheNode) hashtable.get(name);
+ boolean result = false;
+ if (dnsEntry != null)
+ {
+ Collection<? extends DNSEntry> entry = this.getDNSEntryList(dnsEntry.getKey());
+ if (entry != null)
+ {
+ result = entry.remove(dnsEntry);
+ // If we just removed the last one we need to get rid of the entry
+ if (entry.size() == 0)
+ this.remove(dnsEntry.getKey());
+ }
+ }
+ return result;
}
/**
- * List all entries for debugging.
+ * Replace an existing entry by a new one.<br/>
+ * <b>Note:</b> the 2 entries must have the same key.
+ *
+ * @param newDNSEntry
+ * @param existingDNSEntry
+ * @return <code>true</code> if the entry has been replace, <code>false</code> otherwise.
*/
- public synchronized void print()
+ public synchronized boolean replaceDNSEntry(DNSEntry newDNSEntry, DNSEntry existingDNSEntry)
{
- for (final Iterator i = iterator(); i.hasNext();)
+ boolean result = false;
+ if ((newDNSEntry != null) && (existingDNSEntry != null) && (newDNSEntry.getKey().equals(existingDNSEntry.getKey())))
{
- for (CacheNode n = (CacheNode) i.next(); n != null; n = n.next)
+ Map.Entry<String, List<? extends DNSEntry>> oldEntry = this.getEntry(newDNSEntry.getKey());
+
+ List<DNSEntry> aNewValue = null;
+ if (oldEntry != null)
+ {
+ aNewValue = new ArrayList<DNSEntry>(oldEntry.getValue());
+ }
+ else
{
- System.out.println(n.value);
+ aNewValue = new ArrayList<DNSEntry>();
}
+ aNewValue.remove(existingDNSEntry);
+ aNewValue.add(newDNSEntry);
+
+ if (oldEntry != null)
+ {
+ oldEntry.setValue(aNewValue);
+ }
+ else
+ {
+ this.entrySet().add(new _CacheEntry(newDNSEntry.getKey(), aNewValue));
+ }
+ // This is probably not very informative
+ result = true;
}
+ return result;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.AbstractMap#toString()
+ */
+ @Override
public synchronized String toString()
{
- final StringBuffer aLog = new StringBuffer();
+ StringBuffer aLog = new StringBuffer();
aLog.append("\t---- cache ----");
- for (final Iterator i = iterator(); i.hasNext();)
+ for (Map.Entry<String, List<? extends DNSEntry>> entry : this.entrySet())
{
- for (CacheNode n = (CacheNode) i.next(); n != null; n = n.next)
- {
- aLog.append("\n\t\t" + n.value);
- }
+ aLog.append("\n\t\t" + entry.toString());
}
return aLog.toString();
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSConstants.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSConstants.java
deleted file mode 100644
index 849989950..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSConstants.java
+++ /dev/null
@@ -1,127 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-
-package javax.jmdns.impl;
-
-/**
- * DNS constants.
- *
- * @version %I%, %G%
- * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
- */
-public final class DNSConstants
-{
-
- // changed to final class - jeffs
- public final static String MDNS_GROUP = "224.0.0.251";
- public final static String MDNS_GROUP_IPV6 = "FF02::FB";
- public final static int MDNS_PORT = Integer.parseInt(System.getProperty("net.mdns.port", "5353"));
- public final static int DNS_PORT = 53;
- public final static int DNS_TTL = 60 * 60; // default one hour TTL
- // public final static int DNS_TTL = 120 * 60; // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13)
-
- public final static int MAX_MSG_TYPICAL = 1460;
- public final static int MAX_MSG_ABSOLUTE = 8972;
-
- public final static int FLAGS_QR_MASK = 0x8000; // Query response mask
- public final static int FLAGS_QR_QUERY = 0x0000; // Query
- public final static int FLAGS_QR_RESPONSE = 0x8000; // Response
-
- public final static int FLAGS_AA = 0x0400; // Authorative answer
- public final static int FLAGS_TC = 0x0200; // Truncated
- public final static int FLAGS_RD = 0x0100; // Recursion desired
- public final static int FLAGS_RA = 0x8000; // Recursion available
-
- public final static int FLAGS_Z = 0x0040; // Zero
- public final static int FLAGS_AD = 0x0020; // Authentic data
- public final static int FLAGS_CD = 0x0010; // Checking disabled
-
- public final static int CLASS_IN = 1; // public final static Internet
- public final static int CLASS_CS = 2; // CSNET
- public final static int CLASS_CH = 3; // CHAOS
- public final static int CLASS_HS = 4; // Hesiod
- public final static int CLASS_NONE = 254; // Used in DNS UPDATE [RFC 2136]
- public final static int CLASS_ANY = 255; // Not a DNS class, but a DNS query class, meaning "all classes"
- public final static int CLASS_MASK = 0x7FFF; // Multicast DNS uses the bottom 15 bits to identify the record class...
- public final static int CLASS_UNIQUE = 0x8000; // ... and the top bit indicates that all other cached records are now invalid
-
- public final static int TYPE_IGNORE = 0; // This is a hack to stop further processing
- public final static int TYPE_A = 1; // Address
- public final static int TYPE_NS = 2; // Name Server
- public final static int TYPE_MD = 3; // Mail Destination
- public final static int TYPE_MF = 4; // Mail Forwarder
- public final static int TYPE_CNAME = 5; // Canonical Name
- public final static int TYPE_SOA = 6; // Start of Authority
- public final static int TYPE_MB = 7; // Mailbox
- public final static int TYPE_MG = 8; // Mail Group
- public final static int TYPE_MR = 9; // Mail Rename
- public final static int TYPE_NULL = 10; // NULL RR
- public final static int TYPE_WKS = 11; // Well-known-service
- public final static int TYPE_PTR = 12; // Domain Name popublic final static inter
- public final static int TYPE_HINFO = 13; // Host information
- public final static int TYPE_MINFO = 14; // Mailbox information
- public final static int TYPE_MX = 15; // Mail exchanger
- public final static int TYPE_TXT = 16; // Arbitrary text string
- public final static int TYPE_RP = 17; // for Responsible Person [RFC1183]
- public final static int TYPE_AFSDB = 18; // for AFS Data Base location [RFC1183]
- public final static int TYPE_X25 = 19; // for X.25 PSDN address [RFC1183]
- public final static int TYPE_ISDN = 20; // for ISDN address [RFC1183]
- public final static int TYPE_RT = 21; // for Route Through [RFC1183]
- public final static int TYPE_NSAP = 22; // for NSAP address, NSAP style A record [RFC1706]
- public final static int TYPE_NSAP_PTR = 23; //
- public final static int TYPE_SIG = 24; // for security signature [RFC2931]
- public final static int TYPE_KEY = 25; // for security key [RFC2535]
- public final static int TYPE_PX = 26; // X.400 mail mapping information [RFC2163]
- public final static int TYPE_GPOS = 27; // Geographical Position [RFC1712]
- public final static int TYPE_AAAA = 28; // IP6 Address [Thomson]
- public final static int TYPE_LOC = 29; // Location Information [Vixie]
- public final static int TYPE_NXT = 30; // Next Domain - OBSOLETE [RFC2535, RFC3755]
- public final static int TYPE_EID = 31; // Endpoint Identifier [Patton]
- public final static int TYPE_NIMLOC = 32; // Nimrod Locator [Patton]
- public final static int TYPE_SRV = 33; // Server Selection [RFC2782]
- public final static int TYPE_ATMA = 34; // ATM Address [Dobrowski]
- public final static int TYPE_NAPTR = 35; // Naming Authority Pointer [RFC2168, RFC2915]
- public final static int TYPE_KX = 36; // Key Exchanger [RFC2230]
- public final static int TYPE_CERT = 37; // CERT [RFC2538]
- public final static int TYPE_A6 = 38; // A6 [RFC2874]
- public final static int TYPE_DNAME = 39; // DNAME [RFC2672]
- public final static int TYPE_SINK = 40; // SINK [Eastlake]
- public final static int TYPE_OPT = 41; // OPT [RFC2671]
- public final static int TYPE_APL = 42; // APL [RFC3123]
- public final static int TYPE_DS = 43; // Delegation Signer [RFC3658]
- public final static int TYPE_SSHFP = 44; // SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt]
- public final static int TYPE_RRSIG = 46; // RRSIG [RFC3755]
- public final static int TYPE_NSEC = 47; // NSEC [RFC3755]
- public final static int TYPE_DNSKEY = 48; // DNSKEY [RFC3755]
- public final static int TYPE_UINFO = 100; // [IANA-Reserved]
- public final static int TYPE_UID = 101; // [IANA-Reserved]
- public final static int TYPE_GID = 102; // [IANA-Reserved]
- public final static int TYPE_UNSPEC = 103; // [IANA-Reserved]
- public final static int TYPE_TKEY = 249; // Transaction Key [RFC2930]
- public final static int TYPE_TSIG = 250; // Transaction Signature [RFC2845]
- public final static int TYPE_IXFR = 251; // Incremental transfer [RFC1995]
- public final static int TYPE_AXFR = 252; // Transfer of an entire zone [RFC1035]
- public final static int TYPE_MAILA = 253; // Mailbox-related records (MB, MG or MR) [RFC1035]
- public final static int TYPE_MAILB = 254; // Mail agent RRs (Obsolete - see MX) [RFC1035]
- public final static int TYPE_ANY = 255; // Request for all records [RFC1035]
-
- //Time Intervals for various functions
-
- public final static int SHARED_QUERY_TIME = 20; //milliseconds before send shared query
- public final static int QUERY_WAIT_INTERVAL = 225; //milliseconds between query loops.
- public final static int PROBE_WAIT_INTERVAL = 250; //milliseconds between probe loops.
- public final static int RESPONSE_MIN_WAIT_INTERVAL = 20; //minimal wait interval for response.
- public final static int RESPONSE_MAX_WAIT_INTERVAL = 115; //maximal wait interval for response
- public final static int PROBE_CONFLICT_INTERVAL = 1000; //milliseconds to wait after conflict.
- public final static int PROBE_THROTTLE_COUNT = 10; //After x tries go 1 time a sec. on probes.
- public final static int PROBE_THROTTLE_COUNT_INTERVAL = 5000; //We only increment the throttle count, if
- // the previous increment is inside this interval.
- public final static int ANNOUNCE_WAIT_INTERVAL = 1000; //milliseconds between Announce loops.
- public final static int RECORD_REAPER_INTERVAL = 10000; //milliseconds between cache cleanups.
- public final static int KNOWN_ANSWER_TTL = 120;
- public final static int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; // 50% of the TTL in milliseconds
-
- public final static String DNS_META_QUERY = "._dns-sd._udp."; // PTR records, where the rdata of each PTR record is the two-label name of a service type, e.g. "_http._tcp.
-}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSEntry.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSEntry.java
index 8b36eb915..93fd53749 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSEntry.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSEntry.java
@@ -2,147 +2,307 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
package javax.jmdns.impl;
-//import java.util.logging.Logger;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.jmdns.ServiceInfo.Fields;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
/**
- * DNS entry with a name, type, and class. This is the base
- * class for questions and records.
+ * DNS entry with a name, type, and class. This is the base class for questions and records.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Pierre Frisch, Rick Blair
+ * @author Arthur van Hoff, Pierre Frisch, Rick Blair
*/
-public class DNSEntry
+public abstract class DNSEntry
{
-// private static Logger logger = Logger.getLogger(DNSEntry.class.getName());
- String key;
- String name;
- int type;
- int clazz;
- boolean unique;
+ // private static Logger logger = Logger.getLogger(DNSEntry.class.getName());
+ private final String _key;
+
+ private final String _name;
+
+ private final DNSRecordType _type;
+
+ private final DNSRecordClass _dnsClass;
+
+ private final boolean _unique;
+
+ final Map<Fields, String> _qualifiedNameMap;
/**
* Create an entry.
*/
- DNSEntry(String name, int type, int clazz)
+ DNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
{
- this.key = name.toLowerCase();
- this.name = name;
- this.type = type;
- this.clazz = clazz & DNSConstants.CLASS_MASK;
- this.unique = (clazz & DNSConstants.CLASS_UNIQUE) != 0;
+ _name = name;
+ // _key = (name != null ? name.trim().toLowerCase() : null);
+ _type = type;
+ _dnsClass = recordClass;
+ _unique = unique;
+ _qualifiedNameMap = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getName());
+ String domain = _qualifiedNameMap.get(Fields.Domain);
+ String protocol = _qualifiedNameMap.get(Fields.Protocol);
+ String application = _qualifiedNameMap.get(Fields.Application);
+ String instance = _qualifiedNameMap.get(Fields.Instance).toLowerCase();
+ _key = (instance.length() > 0 ? instance + "." : "") + (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
}
- /**
- * Check if two entries have exactly the same name, type, and class.
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
*/
+ @Override
public boolean equals(Object obj)
{
+ boolean result = false;
if (obj instanceof DNSEntry)
{
DNSEntry other = (DNSEntry) obj;
- return name.equals(other.name) && type == other.type && clazz == other.clazz;
+ result = this.getKey().equals(other.getKey()) && this.getRecordType().equals(other.getRecordType()) && _dnsClass == other.getRecordClass();
}
- return false;
+ return result;
+ }
+
+ /**
+ * Check if two entries have exactly the same name, type, and class.
+ *
+ * @param entry
+ *
+ * @return <code>true</code> if the two entries have are for the same record, <code>false</code> otherwise
+ */
+ public boolean isSameEntry(DNSEntry entry)
+ {
+ return this.getKey().equals(entry.getKey()) && this.getRecordType().equals(entry.getRecordType()) && ((DNSRecordClass.CLASS_ANY == entry.getRecordClass()) || this.getRecordClass().equals(entry.getRecordClass()));
+ }
+
+ public boolean sameSubtype(DNSEntry other)
+ {
+ return this.getSubtype().equals(other.getSubtype());
+ }
+
+ /**
+ * Returns the subtype of this entry
+ *
+ * @return subtype of this entry
+ */
+ public String getSubtype()
+ {
+ String subtype = this.getQualifiedNameMap().get(Fields.Subtype);
+ return (subtype != null ? subtype : "");
}
+ /**
+ * Returns the name of this entry
+ *
+ * @return name of this entry
+ */
public String getName()
{
- return name;
+ return (_name != null ? _name : "");
}
- public int getType()
+ /**
+ * Returns the key for this entry. The key is the lower case name.
+ *
+ * @return key for this entry
+ */
+ public String getKey()
{
- return type;
+ return (_key != null ? _key : "");
}
/**
- * Overriden, to return a value which is consistent with the value returned
- * by equals(Object).
+ * @return record type
*/
- public int hashCode()
+ public DNSRecordType getRecordType()
{
- return name.hashCode() + type + clazz;
+ return (_type != null ? _type : DNSRecordType.TYPE_IGNORE);
}
/**
- * Get a string given a clazz.
+ * @return record class
*/
- static String getClazz(int clazz)
+ public DNSRecordClass getRecordClass()
{
- switch (clazz & DNSConstants.CLASS_MASK)
+ return (_dnsClass != null ? _dnsClass : DNSRecordClass.CLASS_UNKNOWN);
+ }
+
+ /**
+ * @return true if unique
+ */
+ public boolean isUnique()
+ {
+ return _unique;
+ }
+
+ public Map<Fields, String> getQualifiedNameMap()
+ {
+ return Collections.unmodifiableMap(_qualifiedNameMap);
+ }
+
+ public boolean isServicesDiscoveryMetaQuery()
+ {
+ return _qualifiedNameMap.get(Fields.Application).equals("dns-sd") && _qualifiedNameMap.get(Fields.Instance).equals("_services");
+ }
+
+ public boolean isDomainDiscoveryQuery()
+ {
+ // b._dns-sd._udp.<domain>.
+ // db._dns-sd._udp.<domain>.
+ // r._dns-sd._udp.<domain>.
+ // dr._dns-sd._udp.<domain>.
+ // lb._dns-sd._udp.<domain>.
+
+ if (_qualifiedNameMap.get(Fields.Application).equals("dns-sd"))
{
- case DNSConstants.CLASS_IN:
- return "in";
- case DNSConstants.CLASS_CS:
- return "cs";
- case DNSConstants.CLASS_CH:
- return "ch";
- case DNSConstants.CLASS_HS:
- return "hs";
- case DNSConstants.CLASS_NONE:
- return "none";
- case DNSConstants.CLASS_ANY:
- return "any";
- default:
- return "?";
+ String name = _qualifiedNameMap.get(Fields.Instance);
+ return name.equals("b") || name.equals("db") || name.equals("r") || name.equals("dr") || name.equals("lb");
}
+ return false;
+ }
+
+ public boolean isReverseLookup()
+ {
+ return _qualifiedNameMap.get(Fields.Domain).endsWith("in-addr.arpa") || _qualifiedNameMap.get(Fields.Domain).endsWith("ip6.arpa");
+ }
+
+ /**
+ * Check if the record is stale, i.e. it has outlived more than half of its TTL.
+ *
+ * @param now
+ * update date
+ * @return <code>true</code> is the record is stale, <code>false</code> otherwise.
+ */
+ public abstract boolean isStale(long now);
+
+ /**
+ * Check if the record is expired.
+ *
+ * @param now
+ * update date
+ * @return <code>true</code> is the record is expired, <code>false</code> otherwise.
+ */
+ public abstract boolean isExpired(long now);
+
+ /**
+ * Check that 2 entries are of the same class.
+ *
+ * @param entry
+ * @return <code>true</code> is the two class are the same, <code>false</code> otherwise.
+ */
+ public boolean isSameRecordClass(DNSEntry entry)
+ {
+ return (entry != null) && (entry.getRecordClass() == this.getRecordClass());
+ }
+
+ /**
+ * Check that 2 entries are of the same type.
+ *
+ * @param entry
+ * @return <code>true</code> is the two type are the same, <code>false</code> otherwise.
+ */
+ public boolean isSameType(DNSEntry entry)
+ {
+ return (entry != null) && (entry.getRecordType() == this.getRecordType());
+ }
+
+ /**
+ * @param dout
+ * @throws IOException
+ */
+ protected void toByteArray(DataOutputStream dout) throws IOException
+ {
+ dout.write(this.getName().getBytes("UTF8"));
+ dout.writeShort(this.getRecordType().indexValue());
+ dout.writeShort(this.getRecordClass().indexValue());
}
/**
- * Get a string given a type.
+ * Creates a byte array representation of this record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
+ *
+ * @return byte array representation
*/
- static String getType(int type)
+ protected byte[] toByteArray()
{
- switch (type)
+ try
{
- case DNSConstants.TYPE_A:
- return "a";
- case DNSConstants.TYPE_AAAA:
- return "aaaa";
- case DNSConstants.TYPE_NS:
- return "ns";
- case DNSConstants.TYPE_MD:
- return "md";
- case DNSConstants.TYPE_MF:
- return "mf";
- case DNSConstants.TYPE_CNAME:
- return "cname";
- case DNSConstants.TYPE_SOA:
- return "soa";
- case DNSConstants.TYPE_MB:
- return "mb";
- case DNSConstants.TYPE_MG:
- return "mg";
- case DNSConstants.TYPE_MR:
- return "mr";
- case DNSConstants.TYPE_NULL:
- return "null";
- case DNSConstants.TYPE_WKS:
- return "wks";
- case DNSConstants.TYPE_PTR:
- return "ptr";
- case DNSConstants.TYPE_HINFO:
- return "hinfo";
- case DNSConstants.TYPE_MINFO:
- return "minfo";
- case DNSConstants.TYPE_MX:
- return "mx";
- case DNSConstants.TYPE_TXT:
- return "txt";
- case DNSConstants.TYPE_SRV:
- return "srv";
- case DNSConstants.TYPE_ANY:
- return "any";
- default:
- return "?";
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ DataOutputStream dout = new DataOutputStream(bout);
+ this.toByteArray(dout);
+ dout.close();
+ return bout.toByteArray();
}
+ catch (IOException e)
+ {
+ throw new InternalError();
+ }
+ }
+
+ /**
+ * Does a lexicographic comparison of the byte array representation of this record and that record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
+ *
+ * @param that
+ * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
+ */
+ public int compareTo(DNSEntry that)
+ {
+ byte[] thisBytes = this.toByteArray();
+ byte[] thatBytes = that.toByteArray();
+ for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); i < n; i++)
+ {
+ if (thisBytes[i] > thatBytes[i])
+ {
+ return 1;
+ }
+ else if (thisBytes[i] < thatBytes[i])
+ {
+ return -1;
+ }
+ }
+ return thisBytes.length - thatBytes.length;
+ }
+
+ /**
+ * Overriden, to return a value which is consistent with the value returned by equals(Object).
+ */
+ @Override
+ public int hashCode()
+ {
+ return _name.hashCode() + this.getRecordType().indexValue() + this.getRecordClass().indexValue();
}
- public String toString(String hdr, String other)
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
{
- return hdr + "[" + getType(type) + "," + getClazz(clazz) + (unique ? "-unique," : ",") + name + ((other != null) ? "," + other + "]" : "]");
+ StringBuilder aLog = new StringBuilder();
+ aLog.append("[" + this.getClass().getSimpleName() + "@" + System.identityHashCode(this));
+ aLog.append(" type: " + this.getRecordType());
+ aLog.append(", class: " + this.getRecordClass());
+ aLog.append((_unique ? "-unique," : ","));
+ aLog.append(" name: " + _name);
+ this.toString(aLog);
+ aLog.append("]");
+ return aLog.toString();
}
+
+ /**
+ * @param aLog
+ */
+ protected void toString(StringBuilder aLog)
+ {
+ // Stub
+ }
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSIncoming.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSIncoming.java
index c9f931c5b..7adc6fe76 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSIncoming.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSIncoming.java
@@ -2,253 +2,393 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
-
package javax.jmdns.impl;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSLabel;
+import javax.jmdns.impl.constants.DNSOptionCode;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+import javax.jmdns.impl.constants.DNSResultCode;
/**
* Parse an incoming DNS message into its components.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert
+ * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert
*/
-public final class DNSIncoming
+public final class DNSIncoming extends DNSMessage
{
-// private static Logger logger = Logger.getLogger(DNSIncoming.class.getName());
-
+ private static Logger logger = Logger.getLogger(DNSIncoming.class.getName());
+
// This is a hack to handle a bug in the BonjourConformanceTest
- // It is sending out target strings that don't follow the "domain name"
- // format.
+ // It is sending out target strings that don't follow the "domain name" format.
public static boolean USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET = true;
-
- // Implementation note: This vector should be immutable.
- // If a client of DNSIncoming changes the contents of this vector,
- // we get undesired results. To fix this, we have to migrate to
- // the Collections API of Java 1.2. i.e we replace Vector by List.
- // final static Vector EMPTY = new Vector();
-
- private DatagramPacket packet;
- private int off;
- private int len;
- private byte data[];
-
- int id;
- private int flags;
- private int numQuestions;
- int numAnswers;
- private int numAuthorities;
- private int numAdditionals;
- private long receivedTime;
-
- private List questions;
- List answers;
+
+ public static class MessageInputStream extends ByteArrayInputStream
+ {
+ // FIXME [PJYF September 9 2010] Future design pattern convert this class to use a custom byte stream.
+
+ /**
+ * @param buffer
+ * @param offset
+ * @param length
+ */
+ public MessageInputStream(byte[] buffer, int offset, int length)
+ {
+ super(buffer, offset, length);
+ }
+
+ }
+
+ private DatagramPacket _packet;
+
+ private int _off;
+
+ private int _len;
+
+ private byte[] _data;
+
+ private long _receivedTime;
+
+ private int _senderUDPPayload;
/**
* Parse a message from a datagram packet.
*/
DNSIncoming(DatagramPacket packet) throws IOException
{
- this.packet = packet;
+ super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
+ this._packet = packet;
InetAddress source = packet.getAddress();
- this.data = packet.getData();
- this.len = packet.getLength();
- this.off = packet.getOffset();
- this.questions = Collections.EMPTY_LIST;
- this.answers = Collections.EMPTY_LIST;
- this.receivedTime = System.currentTimeMillis();
+ this._data = packet.getData();
+ this._len = packet.getLength();
+ this._off = packet.getOffset();
+ this._receivedTime = System.currentTimeMillis();
+ this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
try
{
- id = readUnsignedShort();
- flags = readUnsignedShort();
- numQuestions = readUnsignedShort();
- numAnswers = readUnsignedShort();
- numAuthorities = readUnsignedShort();
- numAdditionals = readUnsignedShort();
+ this.setId(this.readUnsignedShort());
+ this.setFlags(this.readUnsignedShort());
+ int numQuestions = readUnsignedShort();
+ int numAnswers = readUnsignedShort();
+ int numAuthorities = readUnsignedShort();
+ int numAdditionals = readUnsignedShort();
// parse questions
if (numQuestions > 0)
{
- questions = Collections.synchronizedList(new ArrayList(numQuestions));
for (int i = 0; i < numQuestions; i++)
{
- DNSQuestion question = new DNSQuestion(readName(), readUnsignedShort(), readUnsignedShort());
- questions.add(question);
+ _questions.add(this.readQuestion());
}
}
// parse answers
- int n = numAnswers + numAuthorities + numAdditionals;
- if (n > 0)
+ if (numAnswers > 0)
{
- answers = Collections.synchronizedList(new ArrayList(n));
- for (int i = 0; i < n; i++)
+ for (int i = 0; i < numAnswers; i++)
{
- String domain = readName();
- int type = readUnsignedShort();
- int clazz = readUnsignedShort();
- int ttl = readInt();
- int len = readUnsignedShort();
- int end = off + len;
- DNSRecord rec = null;
-
- switch (type)
+ DNSRecord rec = this.readAnswer(source);
+ if (rec != null)
{
- case DNSConstants.TYPE_A: // IPv4
- case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested
- rec = new DNSRecord.Address(domain, type, clazz, ttl, readBytes(off, len));
- break;
- case DNSConstants.TYPE_CNAME:
- case DNSConstants.TYPE_PTR:
- String service = "";
- try {
- service = readName();
- } catch (IOException e){
- // there was a problem reading the service name
- e.printStackTrace();
- }
- rec = new DNSRecord.Pointer(domain, type, clazz, ttl, service);
- break;
- case DNSConstants.TYPE_TXT:
- rec = new DNSRecord.Text(domain, type, clazz, ttl, readBytes(off, len));
- break;
- case DNSConstants.TYPE_SRV:
- int priority = readUnsignedShort();
- int weight = readUnsignedShort();
- int port = readUnsignedShort();
- String target = "";
- try {
- // This is a hack to handle a bug in the BonjourConformanceTest
- // It is sending out target strings that don't follow the "domain name"
- // format.
-
- if(USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET){
- target = readName();
- } else {
- target = readNonNameString();
- }
- } catch (IOException e) {
- // this can happen if the type of the label
- // cannot be handled.
- // down below the offset gets advanced to the end
- // of the record
- e.printStackTrace();
- }
- rec = new DNSRecord.Service(domain, type, clazz, ttl,
- priority, weight, port, target);
- break;
- case DNSConstants.TYPE_HINFO:
- // Maybe we should do something with those
- break;
- default :
-// logger.finer("DNSIncoming() unknown type:" + type);
- break;
+ // Add a record, if we were able to create one.
+ _answers.add(rec);
}
+ }
+ }
+ if (numAuthorities > 0)
+ {
+ for (int i = 0; i < numAuthorities; i++)
+ {
+ DNSRecord rec = this.readAnswer(source);
if (rec != null)
{
- rec.setRecordSource(source);
// Add a record, if we were able to create one.
- answers.add(rec);
+ _authoritativeAnswers.add(rec);
}
- else
+ }
+ }
+
+ if (numAdditionals > 0)
+ {
+ for (int i = 0; i < numAdditionals; i++)
+ {
+ DNSRecord rec = this.readAnswer(source);
+ if (rec != null)
{
- // Addjust the numbers for the skipped record
- if (answers.size() < numAnswers)
- {
- numAnswers--;
- }
- else
- {
- if (answers.size() < numAnswers + numAuthorities)
- {
- numAuthorities--;
- }
- else
- {
- if (answers.size() < numAnswers + numAuthorities + numAdditionals)
- {
- numAdditionals--;
- }
- }
- }
+ // Add a record, if we were able to create one.
+ _additionals.add(rec);
}
- off = end;
}
}
}
catch (IOException e)
{
-// logger.log(Level.WARNING, "DNSIncoming() dump " + print(true) + "\n exception ", e);
+ logger.log(Level.WARNING, "DNSIncoming() dump " + print(true) + "\n exception ", e);
throw e;
}
}
- /**
- * Check if the message is a query.
- */
- boolean isQuery()
+ private DNSQuestion readQuestion() throws IOException
{
- return (flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
+ String domain = this.readName();
+ DNSRecordType type = DNSRecordType.typeForIndex(this.readUnsignedShort());
+ int recordClassIndex = this.readUnsignedShort();
+ DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
+ boolean unique = recordClass.isUnique(recordClassIndex);
+ return DNSQuestion.newQuestion(domain, type, recordClass, unique);
}
- /**
- * Check if the message is truncated.
- */
- public boolean isTruncated()
+ private DNSRecord readAnswer(InetAddress source) throws IOException
{
- return (flags & DNSConstants.FLAGS_TC) != 0;
- }
+ String domain = this.readName();
+ DNSRecordType type = DNSRecordType.typeForIndex(this.readUnsignedShort());
+ int recordClassIndex = this.readUnsignedShort();
+ DNSRecordClass recordClass = (type == DNSRecordType.TYPE_OPT ? DNSRecordClass.CLASS_UNKNOWN : DNSRecordClass.classForIndex(recordClassIndex));
+ boolean unique = recordClass.isUnique(recordClassIndex);
+ int ttl = this.readInt();
+ int len = this.readUnsignedShort();
+ int end = _off + len;
+ DNSRecord rec = null;
+
+ switch (type)
+ {
+ case TYPE_A: // IPv4
+ rec = new DNSRecord.IPv4Address(domain, recordClass, unique, ttl, readBytes(_off, len));
+ _off = _off + len;
+ break;
+ case TYPE_AAAA: // IPv6
+ rec = new DNSRecord.IPv6Address(domain, recordClass, unique, ttl, readBytes(_off, len));
+ _off = _off + len;
+ break;
+ case TYPE_CNAME:
+ case TYPE_PTR:
+ String service = "";
+ try
+ {
+ service = this.readName();
+ }
+ catch (IOException e)
+ {
+ // there was a problem reading the service name
+ logger.log(Level.WARNING, "There was a problem reading the service name of the answer for domain:" + domain, e);
+ }
+ if (service.length() > 0)
+ {
+ rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
+ }
+ else
+ {
+ logger.log(Level.WARNING, "There was a problem reading the service name of the answer for domain:" + domain);
+ }
+ break;
+ case TYPE_TXT:
+ rec = new DNSRecord.Text(domain, recordClass, unique, ttl, readBytes(_off, len));
+ _off = _off + len;
+ break;
+ case TYPE_SRV:
+ int priority = readUnsignedShort();
+ int weight = readUnsignedShort();
+ int port = readUnsignedShort();
+ String target = "";
+ try
+ {
+ // This is a hack to handle a bug in the BonjourConformanceTest
+ // It is sending out target strings that don't follow the "domain name" format.
- /**
- * Check if the message is a response.
- */
- boolean isResponse()
- {
- return (flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_RESPONSE;
+ if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET)
+ {
+ target = readName();
+ }
+ else
+ {
+ target = readNonNameString();
+ }
+ }
+ catch (IOException e)
+ {
+ // this can happen if the type of the label cannot be handled.
+ // down below the offset gets advanced to the end of the record
+ logger.log(Level.WARNING, "There was a problem reading the label of the answer. This can happen if the type of the label cannot be handled." + this, e);
+ }
+ rec = new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
+ break;
+ case TYPE_HINFO:
+ StringBuffer buf = new StringBuffer();
+ this.readUTF(buf, _off, len);
+ int index = buf.indexOf(" ");
+ String cpu = (index > 0 ? buf.substring(0, index) : buf.toString()).trim();
+ String os = (index > 0 ? buf.substring(index + 1) : "").trim();
+ rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
+ break;
+ case TYPE_OPT:
+ DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
+ int version = (ttl & 0x00ff0000) >> 16;
+ if (version == 0)
+ {
+ _senderUDPPayload = recordClassIndex;
+ while (_off < end)
+ {
+ // Read RDData
+ int optionCodeInt = 0;
+ DNSOptionCode optionCode = null;
+ if (end - _off >= 2)
+ {
+ optionCodeInt = this.readUnsignedShort();
+ optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
+ }
+ else
+ {
+ logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
+ break;
+ }
+ int optionLength = 0;
+ if (end - _off >= 2)
+ {
+ optionLength = readUnsignedShort();
+ }
+ else
+ {
+ logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
+ break;
+ }
+ byte[] optiondata = new byte[0];
+ if (end - _off >= optionLength)
+ {
+ optiondata = this.readBytes(_off, optionLength);
+ _off = _off + optionLength;
+ }
+ //
+ if (DNSOptionCode.Unknown == optionCode)
+ {
+ logger.log(Level.WARNING, "There was an OPT answer. Not currently handled. Option code: " + optionCodeInt + " data: " + this._hexString(optiondata));
+ }
+ else
+ {
+ // We should really do something with those options.
+ switch (optionCode)
+ {
+ case Owner:
+ // Valid length values are 8, 14, 18 and 20
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // |Opt|Len|V|S|Primary MAC|Wakeup MAC | Password |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ //
+ int ownerVersion = 0;
+ int ownerSequence = 0;
+ byte[] ownerPrimaryMacAddress = null;
+ byte[] ownerWakeupMacAddress = null;
+ byte[] ownerPassword = null;
+ try
+ {
+ ownerVersion = optiondata[0];
+ ownerSequence = optiondata[1];
+ ownerPrimaryMacAddress = new byte[] { optiondata[2], optiondata[3], optiondata[4], optiondata[5], optiondata[6], optiondata[7] };
+ ownerWakeupMacAddress = ownerPrimaryMacAddress;
+ if (optiondata.length > 8)
+ {
+ // We have a wakeupMacAddress.
+ ownerWakeupMacAddress = new byte[] { optiondata[8], optiondata[9], optiondata[10], optiondata[11], optiondata[12], optiondata[13] };
+ }
+ if (optiondata.length == 18)
+ {
+ // We have a short password.
+ ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17] };
+ }
+ if (optiondata.length == 22)
+ {
+ // We have a long password.
+ ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17], optiondata[18], optiondata[19], optiondata[20], optiondata[21] };
+ }
+ }
+ catch (Exception exception)
+ {
+ logger.warning("Malformed OPT answer. Option code: Owner data: " + this._hexString(optiondata));
+ }
+ logger.info("Unhandled Owner OPT version: " + ownerVersion + " sequence: " + ownerSequence + " MAC address: " + this._hexString(ownerPrimaryMacAddress)
+ + (ownerWakeupMacAddress != ownerPrimaryMacAddress ? " wakeup MAC address: " + this._hexString(ownerWakeupMacAddress) : "")
+ + (ownerPassword != null ? " password: " + this._hexString(ownerPassword) : ""));
+ break;
+ case LLQ:
+ case NSID:
+ case UL:
+ case Unknown:
+ logger.log(Level.INFO, "There was an OPT answer. Option code: " + optionCode + " data: " + this._hexString(optiondata));
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ logger.log(Level.WARNING, "There was an OPT answer. Wrong version number: " + version + " result code: " + extendedResultCode);
+ }
+ break;
+ default:
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("DNSIncoming() unknown type:" + type);
+ }
+ _off = end;
+ break;
+ }
+ if (rec != null)
+ {
+ rec.setRecordSource(source);
+ }
+ _off = end;
+ return rec;
}
private int get(int off) throws IOException
{
- if ((off < 0) || (off >= len))
+ if ((off < 0) || (off >= _len))
{
throw new IOException("parser error: offset=" + off);
}
- return data[off] & 0xFF;
+ return _data[off] & 0xFF;
}
private int readUnsignedShort() throws IOException
{
- return (get(off++) << 8) + get(off++);
+ return (this.get(_off++) << 8) | this.get(_off++);
}
private int readInt() throws IOException
{
- return (readUnsignedShort() << 16) + readUnsignedShort();
+ return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
}
+ /**
+ * @param off
+ * @param len
+ * @return
+ * @throws IOException
+ */
private byte[] readBytes(int off, int len) throws IOException
{
byte bytes[] = new byte[len];
- System.arraycopy(data, off, bytes, 0, len);
+ if (len > 0)
+ System.arraycopy(_data, off, bytes, 0, len);
return bytes;
}
private void readUTF(StringBuffer buf, int off, int len) throws IOException
{
- for (int end = off + len; off < end;)
+ int offset = off;
+ for (int end = offset + len; offset < end;)
{
- int ch = get(off++);
+ int ch = get(offset++);
switch (ch >> 4)
{
case 0:
@@ -263,16 +403,16 @@ public final class DNSIncoming
break;
case 12:
case 13:
- // 110x xxxx 10xx xxxx
- ch = ((ch & 0x1F) << 6) | (get(off++) & 0x3F);
+ // 110x xxxx 10xx xxxx
+ ch = ((ch & 0x1F) << 6) | (get(offset++) & 0x3F);
break;
case 14:
- // 1110 xxxx 10xx xxxx 10xx xxxx
- ch = ((ch & 0x0f) << 12) | ((get(off++) & 0x3F) << 6) | (get(off++) & 0x3F);
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ ch = ((ch & 0x0f) << 12) | ((get(offset++) & 0x3F) << 6) | (get(offset++) & 0x3F);
break;
default:
- // 10xx xxxx, 1111 xxxx
- ch = ((ch & 0x3F) << 4) | (get(off++) & 0x0f);
+ // 10xx xxxx, 1111 xxxx
+ ch = ((ch & 0x3F) << 4) | (get(offset++) & 0x0f);
break;
}
buf.append((char) ch);
@@ -282,17 +422,18 @@ public final class DNSIncoming
private String readNonNameString() throws IOException
{
StringBuffer buf = new StringBuffer();
- int off = this.off;
+ int off = this._off;
int len = get(off++);
readUTF(buf, off, len);
+ this._off = this._off + len + 1;
return buf.toString();
}
-
+
private String readName() throws IOException
{
StringBuffer buf = new StringBuffer();
- int off = this.off;
+ int off = this._off;
int next = -1;
int first = off;
@@ -303,34 +444,37 @@ public final class DNSIncoming
{
break;
}
- switch (len & 0xC0)
+ switch (DNSLabel.labelForByte(len))
{
- case 0x00:
- //buf.append("[" + off + "]");
- readUTF(buf, off, len);
+ case Standard:
+ // buf.append("[" + off + "]");
+ this.readUTF(buf, off, len);
off += len;
buf.append('.');
break;
- case 0xC0:
- //buf.append("<" + (off - 1) + ">");
+ case Compressed:
+ // buf.append("<" + (off - 1) + ">");
if (next < 0)
{
next = off + 1;
}
- off = ((len & 0x3F) << 8) | get(off++);
+ off = (DNSLabel.labelValue(len) << 8) | this.get(off++);
if (off >= first)
{
- throw new IOException("bad domain name: possible circular name detected." +
- " name start: " + first +
- " bad offset: 0x" + Integer.toHexString(off));
+ throw new IOException("bad domain name: possible circular name detected." + " name start: " + first + " bad offset: 0x" + Integer.toHexString(off));
}
first = off;
break;
+ case Extended:
+ // int extendedLabelClass = DNSLabel.labelValue(len);
+ logger.severe("Extended label are not currently supported.");
+ break;
+ case Unknown:
default:
- throw new IOException("unsupported dns label type: '" + Integer.toHexString(len & 0xC0) +"' at " + (off-1));
+ throw new IOException("unsupported dns label type: '" + Integer.toHexString(len & 0xC0) + "' at " + (off - 1));
}
}
- this.off = (next >= 0) ? next : off;
+ this._off = (next >= 0) ? next : off;
return buf.toString();
}
@@ -339,72 +483,63 @@ public final class DNSIncoming
*/
String print(boolean dump)
{
- StringBuffer buf = new StringBuffer();
- buf.append(toString() + "\n");
- for (Iterator iterator = questions.iterator(); iterator.hasNext();)
- {
- buf.append(" ques:" + iterator.next() + "\n");
- }
- int count = 0;
- for (Iterator iterator = answers.iterator(); iterator.hasNext(); count++)
- {
- if (count < numAnswers)
- {
- buf.append(" answ:");
- }
- else
- {
- if (count < numAnswers + numAuthorities)
- {
- buf.append(" auth:");
- }
- else
- {
- buf.append(" addi:");
- }
- }
- buf.append(iterator.next() + "\n");
- }
+ StringBuilder buf = new StringBuilder();
+ buf.append(this.print());
if (dump)
{
- for (int off = 0, len = packet.getLength(); off < len; off += 32)
+ for (int off = 0, len = _packet.getLength(); off < len; off += 32)
{
int n = Math.min(32, len - off);
- if (off < 10)
+ if (off < 0x10)
{
buf.append(' ');
}
- if (off < 100)
+ if (off < 0x100)
{
buf.append(' ');
}
- buf.append(off);
+ if (off < 0x1000)
+ {
+ buf.append(' ');
+ }
+ buf.append(Integer.toHexString(off));
buf.append(':');
- for (int i = 0; i < n; i++)
+ int index = 0;
+ for (index = 0; index < n; index++)
{
- if ((i % 8) == 0)
+ if ((index % 8) == 0)
{
buf.append(' ');
}
- buf.append(Integer.toHexString((data[off + i] & 0xF0) >> 4));
- buf.append(Integer.toHexString((data[off + i] & 0x0F) >> 0));
+ buf.append(Integer.toHexString((_data[off + index] & 0xF0) >> 4));
+ buf.append(Integer.toHexString((_data[off + index] & 0x0F) >> 0));
+ }
+ // for incomplete lines
+ if (index < 32)
+ {
+ for (int i = index; i < 32; i++)
+ {
+ if ((i % 8) == 0)
+ {
+ buf.append(' ');
+ }
+ buf.append(" ");
+ }
}
- buf.append("\n");
buf.append(" ");
- for (int i = 0; i < n; i++)
+ for (index = 0; index < n; index++)
{
- if ((i % 8) == 0)
+ if ((index % 8) == 0)
{
buf.append(' ');
}
- buf.append(' ');
- int ch = data[off + i] & 0xFF;
+ int ch = _data[off + index] & 0xFF;
buf.append(((ch > ' ') && (ch < 127)) ? (char) ch : '.');
}
buf.append("\n");
// limit message size
- if (off + 32 >= 256)
+ if (off + 32 >= 2048)
{
buf.append("....\n");
break;
@@ -414,56 +549,89 @@ public final class DNSIncoming
return buf.toString();
}
+ @Override
public String toString()
{
StringBuffer buf = new StringBuffer();
buf.append(isQuery() ? "dns[query," : "dns[response,");
- if (packet.getAddress() != null)
+ if (_packet.getAddress() != null)
{
- buf.append(packet.getAddress().getHostAddress());
+ buf.append(_packet.getAddress().getHostAddress());
}
buf.append(':');
- buf.append(packet.getPort());
- buf.append(",len=");
- buf.append(packet.getLength());
- buf.append(",id=0x");
- buf.append(Integer.toHexString(id));
- if (flags != 0)
+ buf.append(_packet.getPort());
+ buf.append(", length=");
+ buf.append(_packet.getLength());
+ buf.append(", id=0x");
+ buf.append(Integer.toHexString(this.getId()));
+ if (this.getFlags() != 0)
{
- buf.append(",flags=0x");
- buf.append(Integer.toHexString(flags));
- if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0)
+ buf.append(", flags=0x");
+ buf.append(Integer.toHexString(this.getFlags()));
+ if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0)
{
buf.append(":r");
}
- if ((flags & DNSConstants.FLAGS_AA) != 0)
+ if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0)
{
buf.append(":aa");
}
- if ((flags & DNSConstants.FLAGS_TC) != 0)
+ if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0)
{
buf.append(":tc");
}
}
- if (numQuestions > 0)
+ if (this.getNumberOfQuestions() > 0)
+ {
+ buf.append(", questions=");
+ buf.append(this.getNumberOfQuestions());
+ }
+ if (this.getNumberOfAnswers() > 0)
{
- buf.append(",questions=");
- buf.append(numQuestions);
+ buf.append(", answers=");
+ buf.append(this.getNumberOfAnswers());
}
- if (numAnswers > 0)
+ if (this.getNumberOfAuthorities() > 0)
{
- buf.append(",answers=");
- buf.append(numAnswers);
+ buf.append(", authorities=");
+ buf.append(this.getNumberOfAuthorities());
}
- if (numAuthorities > 0)
+ if (this.getNumberOfAdditionals() > 0)
{
- buf.append(",authorities=");
- buf.append(numAuthorities);
+ buf.append(", additionals=");
+ buf.append(this.getNumberOfAdditionals());
}
- if (numAdditionals > 0)
+ if (this.getNumberOfQuestions() > 0)
{
- buf.append(",additionals=");
- buf.append(numAdditionals);
+ buf.append("\nquestions:");
+ for (DNSQuestion question : _questions)
+ {
+ buf.append("\n\t" + question);
+ }
+ }
+ if (this.getNumberOfAnswers() > 0)
+ {
+ buf.append("\nanswers:");
+ for (DNSRecord record : _answers)
+ {
+ buf.append("\n\t" + record);
+ }
+ }
+ if (this.getNumberOfAuthorities() > 0)
+ {
+ buf.append("\nauthorities:");
+ for (DNSRecord record : _authoritativeAnswers)
+ {
+ buf.append("\n\t" + record);
+ }
+ }
+ if (this.getNumberOfAdditionals() > 0)
+ {
+ buf.append("\nadditionals:");
+ for (DNSRecord record : _additionals)
+ {
+ buf.append("\n\t" + record);
+ }
}
buf.append("]");
return buf.toString();
@@ -472,40 +640,17 @@ public final class DNSIncoming
/**
* Appends answers to this Incoming.
*
- * @throws IllegalArgumentException If not a query or if Truncated.
+ * @throws IllegalArgumentException
+ * If not a query or if Truncated.
*/
void append(DNSIncoming that)
{
if (this.isQuery() && this.isTruncated() && that.isQuery())
{
- if (that.numQuestions > 0) {
- if (Collections.EMPTY_LIST.equals(this.questions))
- this.questions = Collections.synchronizedList(new ArrayList(that.numQuestions));
-
- this.questions.addAll(that.questions);
- this.numQuestions += that.numQuestions;
- }
-
- if (Collections.EMPTY_LIST.equals(answers))
- {
- answers = Collections.synchronizedList(new ArrayList());
- }
-
- if (that.numAnswers > 0)
- {
- this.answers.addAll(this.numAnswers, that.answers.subList(0, that.numAnswers));
- this.numAnswers += that.numAnswers;
- }
- if (that.numAuthorities > 0)
- {
- this.answers.addAll(this.numAnswers + this.numAuthorities, that.answers.subList(that.numAnswers, that.numAnswers + that.numAuthorities));
- this.numAuthorities += that.numAuthorities;
- }
- if (that.numAdditionals > 0)
- {
- this.answers.addAll(that.answers.subList(that.numAnswers + that.numAuthorities, that.numAnswers + that.numAuthorities + that.numAdditionals));
- this.numAdditionals += that.numAdditionals;
- }
+ this._questions.addAll(that.getQuestions());
+ this._answers.addAll(that.getAnswers());
+ this._authoritativeAnswers.addAll(that.getAuthorities());
+ this._additionals.addAll(that.getAdditionals());
}
else
{
@@ -515,16 +660,41 @@ public final class DNSIncoming
public int elapseSinceArrival()
{
- return (int) (System.currentTimeMillis() - receivedTime);
+ return (int) (System.currentTimeMillis() - _receivedTime);
}
- public List getQuestions()
+ /**
+ * This will return the default UDP payload except if an OPT record was found with a different size.
+ *
+ * @return the senderUDPPayload
+ */
+ public int getSenderUDPPayload()
{
- return questions;
+ return this._senderUDPPayload;
}
- public List getAnswers()
+ private static final char[] _nibbleToHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ /**
+ * Returns a hex-string for printing
+ *
+ * @param bytes
+ *
+ * @return Returns a hex-string which can be used within a SQL expression
+ */
+ private String _hexString(byte[] bytes)
{
- return answers;
+
+ StringBuilder result = new StringBuilder(2 * bytes.length);
+
+ for (int i = 0; i < bytes.length; i++)
+ {
+ int b = bytes[i] & 0xFF;
+ result.append(_nibbleToHex[b / 16]);
+ result.append(_nibbleToHex[b % 16]);
+ }
+
+ return result.toString();
}
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSListener.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSListener.java
index 4ea15e2d0..f373951b7 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSListener.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSListener.java
@@ -2,23 +2,28 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
package javax.jmdns.impl;
// REMIND: Listener should follow Java idiom for listener or have a different
// name.
/**
- * DNSListener.
- * Listener for record updates.
+ * DNSListener. Listener for record updates.
*
* @author Werner Randelshofer, Rick Blair
- * @version 1.0 May 22, 2004 Created.
+ * @version 1.0 May 22, 2004 Created.
*/
interface DNSListener
{
/**
* Update a DNS record.
+ *
+ * @param dnsCache
+ * record cache
+ * @param now
+ * update date
+ * @param record
+ * DNS record
*/
- void updateRecord(JmDNSImpl jmdns, long now, DNSRecord record);
+ void updateRecord(DNSCache dnsCache, long now, DNSEntry record);
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSMessage.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSMessage.java
new file mode 100644
index 000000000..4455254f1
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSMessage.java
@@ -0,0 +1,267 @@
+/**
+ *
+ */
+package javax.jmdns.impl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jmdns.impl.constants.DNSConstants;
+
+/**
+ * DNSMessage define a DNS message either incoming or outgoing.
+ *
+ * @version %I%, %G%
+ * @author Werner Randelshofer, Rick Blair, Pierre Frisch
+ */
+public abstract class DNSMessage
+{
+
+ /**
+ *
+ */
+ public static final boolean MULTICAST = true;
+
+ /**
+ *
+ */
+ public static final boolean UNICAST = false;
+
+ // protected DatagramPacket _packet;
+ // protected int _off;
+ // protected int _len;
+ // protected byte[] _data;
+
+ private int _id;
+
+ boolean _multicast;
+
+ private int _flags;
+
+ protected final List<DNSQuestion> _questions;
+
+ protected final List<DNSRecord> _answers;
+
+ protected final List<DNSRecord> _authoritativeAnswers;
+
+ protected final List<DNSRecord> _additionals;
+
+ /**
+ * @param flags
+ * @param id
+ * @param multicast
+ */
+ protected DNSMessage(int flags, int id, boolean multicast)
+ {
+ super();
+ _flags = flags;
+ _id = id;
+ _multicast = multicast;
+ _questions = Collections.synchronizedList(new LinkedList<DNSQuestion>());
+ _answers = Collections.synchronizedList(new LinkedList<DNSRecord>());
+ _authoritativeAnswers = Collections.synchronizedList(new LinkedList<DNSRecord>());
+ _additionals = Collections.synchronizedList(new LinkedList<DNSRecord>());
+ }
+
+ // public DatagramPacket getPacket() {
+ // return _packet;
+ // }
+ //
+ // public int getOffset() {
+ // return _off;
+ // }
+ //
+ // public int getLength() {
+ // return _len;
+ // }
+ //
+ // public byte[] getData() {
+ // if ( _data == null ) _data = new byte[DNSConstants.MAX_MSG_TYPICAL];
+ // return _data;
+ // }
+
+ /**
+ * @return message id
+ */
+ public int getId()
+ {
+ return (_multicast ? 0 : _id);
+ }
+
+ /**
+ * @param id
+ * the id to set
+ */
+ public void setId(int id)
+ {
+ this._id = id;
+ }
+
+ /**
+ * @return message flags
+ */
+ public int getFlags()
+ {
+ return _flags;
+ }
+
+ /**
+ * @param flags
+ * the flags to set
+ */
+ public void setFlags(int flags)
+ {
+ this._flags = flags;
+ }
+
+ /**
+ * @return true if multicast
+ */
+ public boolean isMulticast()
+ {
+ return _multicast;
+ }
+
+ /**
+ * @return list of questions
+ */
+ public Collection<? extends DNSQuestion> getQuestions()
+ {
+ return _questions;
+ }
+
+ /**
+ * @return number of questions in the message
+ */
+ public int getNumberOfQuestions()
+ {
+ return this.getQuestions().size();
+ }
+
+ public Collection<? extends DNSRecord> getAllAnswers()
+ {
+ List<DNSRecord> aList = new ArrayList<DNSRecord>(_answers.size() + _authoritativeAnswers.size() + _additionals.size());
+ aList.addAll(_answers);
+ aList.addAll(_authoritativeAnswers);
+ aList.addAll(_additionals);
+ return aList;
+ }
+
+ /**
+ * @return list of answers
+ */
+ public Collection<? extends DNSRecord> getAnswers()
+ {
+ return _answers;
+ }
+
+ /**
+ * @return number of answers in the message
+ */
+ public int getNumberOfAnswers()
+ {
+ return this.getAnswers().size();
+ }
+
+ /**
+ * @return list of authorities
+ */
+ public Collection<? extends DNSRecord> getAuthorities()
+ {
+ return _authoritativeAnswers;
+ }
+
+ /**
+ * @return number of authorities in the message
+ */
+ public int getNumberOfAuthorities()
+ {
+ return this.getAuthorities().size();
+ }
+
+ /**
+ * @return list of additional answers
+ */
+ public Collection<? extends DNSRecord> getAdditionals()
+ {
+ return _additionals;
+ }
+
+ /**
+ * @return number of additional in the message
+ */
+ public int getNumberOfAdditionals()
+ {
+ return this.getAdditionals().size();
+ }
+
+ /**
+ * Check if the message is truncated.
+ *
+ * @return true if the message was truncated
+ */
+ public boolean isTruncated()
+ {
+ return (_flags & DNSConstants.FLAGS_TC) != 0;
+ }
+
+ /**
+ * Check if the message is a query.
+ *
+ * @return true is the message is a query
+ */
+ public boolean isQuery()
+ {
+ return (_flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
+ }
+
+ /**
+ * Check if the message is a response.
+ *
+ * @return true is the message is a response
+ */
+ public boolean isResponse()
+ {
+ return (_flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_RESPONSE;
+ }
+
+ /**
+ * Check if the message is empty
+ *
+ * @return true is the message is empty
+ */
+ public boolean isEmpty()
+ {
+ return (this.getNumberOfQuestions() + this.getNumberOfAnswers() + this.getNumberOfAuthorities() + this.getNumberOfAdditionals()) == 0;
+ }
+
+ /**
+ * Debugging.
+ */
+ String print()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append(this.toString() + "\n");
+ for (DNSQuestion question : _questions)
+ {
+ buf.append("\tquestion: " + question + "\n");
+ }
+ for (DNSRecord answer : _answers)
+ {
+ buf.append("\tanswer: " + answer + "\n");
+ }
+ for (DNSRecord answer : _authoritativeAnswers)
+ {
+ buf.append("\tauthoritative: " + answer + "\n");
+ }
+ for (DNSRecord answer : _additionals)
+ {
+ buf.append("\tadditional: " + answer + "\n");
+ }
+ return buf.toString();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSOutgoing.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSOutgoing.java
index ba27ab012..31822e88f 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSOutgoing.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSOutgoing.java
@@ -2,393 +2,510 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
package javax.jmdns.impl;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.util.Hashtable;
-import java.util.LinkedList;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
/**
* An outgoing DNS message.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Rick Blair, Werner Randelshofer
+ * @author Arthur van Hoff, Rick Blair, Werner Randelshofer
*/
-public final class DNSOutgoing
+public final class DNSOutgoing extends DNSMessage
{
- /**
- * This can be used to turn off domain name compression. This was helpful for
- * tracking problems interacting with other mdns implementations.
- */
- public static boolean USE_DOMAIN_NAME_COMPRESSION = true;
-
-// private static Logger logger = Logger.getLogger(DNSOutgoing.class.getName());
- int id;
- int flags;
- private boolean multicast;
- private int numQuestions;
- private int numAnswers;
- private int numAuthorities;
- private int numAdditionals;
- private Hashtable names;
-
- byte data[];
- int off;
- int len;
- /**
- * Create an outgoing multicast query or response.
- */
- public DNSOutgoing(int flags)
+ public static class MessageOutputStream extends ByteArrayOutputStream
{
- this(flags, true);
- }
+ private DNSOutgoing _out;
+
+ /**
+ * Creates a new message stream, with a buffer capacity of the specified size, in bytes.
+ *
+ * @param size
+ * the initial size.
+ * @exception IllegalArgumentException
+ * if size is negative.
+ */
+ MessageOutputStream(int size, DNSOutgoing out)
+ {
+ super(size);
+ _out = out;
+ }
- /**
- * Create an outgoing query or response.
- */
- public DNSOutgoing(int flags, boolean multicast)
- {
- this.flags = flags;
- this.multicast = multicast;
- names = new Hashtable();
- data = new byte[DNSConstants.MAX_MSG_TYPICAL];
- off = 12;
- }
+ void writeByte(int value)
+ {
+ this.write(value & 0xFF);
+ }
- /**
- * Add a question to the message.
- */
- public void addQuestion(DNSQuestion rec) throws IOException
- {
- if (numAnswers > 0 || numAuthorities > 0 || numAdditionals > 0)
+ void writeBytes(String str, int off, int len)
{
- throw new IllegalStateException("Questions must be added before answers");
+ for (int i = 0; i < len; i++)
+ {
+ writeByte(str.charAt(off + i));
+ }
}
- numQuestions++;
- writeQuestion(rec);
- }
- /**
- * Add an answer if it is not suppressed.
- */
- void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException
- {
- if (numAuthorities > 0 || numAdditionals > 0)
+ void writeBytes(byte data[])
{
- throw new IllegalStateException("Answers must be added before authorities and additionals");
+ if (data != null)
+ {
+ writeBytes(data, 0, data.length);
+ }
}
- if (!rec.suppressedBy(in))
+
+ void writeBytes(byte data[], int off, int len)
{
- addAnswer(rec, 0);
+ for (int i = 0; i < len; i++)
+ {
+ writeByte(data[off + i]);
+ }
}
- }
- /**
- * Add an additional answer to the record. Omit if there is no room.
- */
- void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException
- {
- if ((off < DNSConstants.MAX_MSG_TYPICAL - 200) && !rec.suppressedBy(in))
+ void writeShort(int value)
{
- writeRecord(rec, 0);
- numAdditionals++;
+ writeByte(value >> 8);
+ writeByte(value);
}
- }
- /**
- * Add an answer to the message.
- */
- public void addAnswer(DNSRecord rec, long now) throws IOException
- {
- if (numAuthorities > 0 || numAdditionals > 0)
+ void writeInt(int value)
{
- throw new IllegalStateException("Questions must be added before answers");
+ writeShort(value >> 16);
+ writeShort(value);
}
- if (rec != null)
+
+ void writeUTF(String str, int off, int len)
{
- if ((now == 0) || !rec.isExpired(now))
+ // compute utf length
+ int utflen = 0;
+ for (int i = 0; i < len; i++)
{
- writeRecord(rec, now);
- numAnswers++;
+ int ch = str.charAt(off + i);
+ if ((ch >= 0x0001) && (ch <= 0x007F))
+ {
+ utflen += 1;
+ }
+ else
+ {
+ if (ch > 0x07FF)
+ {
+ utflen += 3;
+ }
+ else
+ {
+ utflen += 2;
+ }
+ }
+ }
+ // write utf length
+ writeByte(utflen);
+ // write utf data
+ for (int i = 0; i < len; i++)
+ {
+ int ch = str.charAt(off + i);
+ if ((ch >= 0x0001) && (ch <= 0x007F))
+ {
+ writeByte(ch);
+ }
+ else
+ {
+ if (ch > 0x07FF)
+ {
+ writeByte(0xE0 | ((ch >> 12) & 0x0F));
+ writeByte(0x80 | ((ch >> 6) & 0x3F));
+ writeByte(0x80 | ((ch >> 0) & 0x3F));
+ }
+ else
+ {
+ writeByte(0xC0 | ((ch >> 6) & 0x1F));
+ writeByte(0x80 | ((ch >> 0) & 0x3F));
+ }
+ }
}
}
- }
- private LinkedList authorativeAnswers = new LinkedList();
-
- /**
- * Add an authorative answer to the message.
- */
- public void addAuthorativeAnswer(DNSRecord rec) throws IOException
- {
- if (numAdditionals > 0)
+ void writeName(String name)
{
- throw new IllegalStateException("Authorative answers must be added before additional answers");
+ writeName(name, true);
}
- authorativeAnswers.add(rec);
- writeRecord(rec, 0);
- numAuthorities++;
- // VERIFY:
+ void writeName(String name, boolean useCompression)
+ {
+ String aName = name;
+ while (true)
+ {
+ int n = aName.indexOf('.');
+ if (n < 0)
+ {
+ n = aName.length();
+ }
+ if (n <= 0)
+ {
+ writeByte(0);
+ return;
+ }
+ if (useCompression && USE_DOMAIN_NAME_COMPRESSION)
+ {
+ Integer offset = _out._names.get(aName);
+ if (offset != null)
+ {
+ int val = offset.intValue();
- }
+ writeByte((val >> 8) | 0xC0);
+ writeByte(val & 0xFF);
+ return;
+ }
+ _out._names.put(aName, Integer.valueOf(this.size()));
+ }
+ writeUTF(aName, 0, n);
+ aName = aName.substring(n);
+ if (aName.startsWith("."))
+ {
+ aName = aName.substring(1);
+ }
+ }
+ }
- void writeByte(int value) throws IOException
- {
- if (off >= data.length)
+ void writeQuestion(DNSQuestion question)
{
- throw new IOException("buffer full");
+ writeName(question.getName());
+ writeShort(question.getRecordType().indexValue());
+ writeShort(question.getRecordClass().indexValue());
}
- data[off++] = (byte) value;
- }
- void writeBytes(String str, int off, int len) throws IOException
- {
- for (int i = 0; i < len; i++)
+ void writeRecord(DNSRecord rec, long now)
{
- writeByte(str.charAt(off + i));
+ writeName(rec.getName());
+ writeShort(rec.getRecordType().indexValue());
+ writeShort(rec.getRecordClass().indexValue() | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0));
+ writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now));
+
+ MessageOutputStream record = new MessageOutputStream(512, _out);
+ rec.write(record);
+ byte[] byteArray = record.toByteArray();
+
+ writeShort(byteArray.length);
+ write(byteArray, 0, byteArray.length);
}
+
}
- void writeBytes(byte data[]) throws IOException
+ /**
+ * This can be used to turn off domain name compression. This was helpful for tracking problems interacting with other mdns implementations.
+ */
+ public static boolean USE_DOMAIN_NAME_COMPRESSION = true;
+
+ Map<String, Integer> _names;
+
+ private int _maxUDPPayload;
+
+ private final MessageOutputStream _questionsBytes;
+
+ private final MessageOutputStream _answersBytes;
+
+ private final MessageOutputStream _authoritativeAnswersBytes;
+
+ private final MessageOutputStream _additionalsAnswersBytes;
+
+ private final static int HEADER_SIZE = 12;
+
+ /**
+ * Create an outgoing multicast query or response.
+ *
+ * @param flags
+ */
+ public DNSOutgoing(int flags)
{
- if (data != null)
- {
- writeBytes(data, 0, data.length);
- }
+ this(flags, true, DNSConstants.MAX_MSG_TYPICAL);
}
- void writeBytes(byte data[], int off, int len) throws IOException
+ /**
+ * Create an outgoing query or response.
+ *
+ * @param flags
+ * @param multicast
+ */
+ public DNSOutgoing(int flags, boolean multicast)
{
- for (int i = 0; i < len; i++)
- {
- writeByte(data[off + i]);
- }
+ this(flags, multicast, DNSConstants.MAX_MSG_TYPICAL);
}
- void writeShort(int value) throws IOException
+ /**
+ * Create an outgoing query or response.
+ *
+ * @param flags
+ * @param multicast
+ * @param senderUDPPayload
+ * The sender's UDP payload size is the number of bytes of the largest UDP payload that can be reassembled and delivered in the sender's network stack.
+ */
+ public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload)
{
- writeByte(value >> 8);
- writeByte(value);
+ super(flags, 0, multicast);
+ _names = new HashMap<String, Integer>();
+ _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL);
+ _questionsBytes = new MessageOutputStream(senderUDPPayload, this);
+ _answersBytes = new MessageOutputStream(senderUDPPayload, this);
+ _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
+ _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
}
- void writeInt(int value) throws IOException
+ /**
+ * Return the number of byte available in the message.
+ *
+ * @return available space
+ */
+ public int availableSpace()
{
- writeShort(value >> 16);
- writeShort(value);
+ return _maxUDPPayload - HEADER_SIZE - _questionsBytes.size() - _answersBytes.size() - _authoritativeAnswersBytes.size() - _additionalsAnswersBytes.size();
}
- void writeUTF(String str, int off, int len) throws IOException
+ /**
+ * Add a question to the message.
+ *
+ * @param rec
+ * @throws IOException
+ */
+ public void addQuestion(DNSQuestion rec) throws IOException
{
- // compute utf length
- int utflen = 0;
- for (int i = 0; i < len; i++)
+ MessageOutputStream record = new MessageOutputStream(512, this);
+ record.writeQuestion(rec);
+ byte[] byteArray = record.toByteArray();
+ if (byteArray.length < this.availableSpace())
{
- int ch = str.charAt(off + i);
- if ((ch >= 0x0001) && (ch <= 0x007F))
- {
- utflen += 1;
- }
- else
- {
- if (ch > 0x07FF)
- {
- utflen += 3;
- }
- else
- {
- utflen += 2;
- }
- }
+ _questions.add(rec);
+ _questionsBytes.write(byteArray, 0, byteArray.length);
}
- // write utf length
- writeByte(utflen);
- // write utf data
- for (int i = 0; i < len; i++)
+ else
{
- int ch = str.charAt(off + i);
- if ((ch >= 0x0001) && (ch <= 0x007F))
- {
- writeByte(ch);
- }
- else
- {
- if (ch > 0x07FF)
- {
- writeByte(0xE0 | ((ch >> 12) & 0x0F));
- writeByte(0x80 | ((ch >> 6) & 0x3F));
- writeByte(0x80 | ((ch >> 0) & 0x3F));
- }
- else
- {
- writeByte(0xC0 | ((ch >> 6) & 0x1F));
- writeByte(0x80 | ((ch >> 0) & 0x3F));
- }
- }
+ throw new IOException("message full");
}
}
- void writeName(String name) throws IOException
+ /**
+ * Add an answer if it is not suppressed.
+ *
+ * @param in
+ * @param rec
+ * @throws IOException
+ */
+ public void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException
{
- writeName(name, true);
+ if ((in == null) || !rec.suppressedBy(in))
+ {
+ this.addAnswer(rec, 0);
+ }
}
-
- void writeName(String name, boolean useCompression) throws IOException
+
+ /**
+ * Add an answer to the message.
+ *
+ * @param rec
+ * @param now
+ * @throws IOException
+ */
+ public void addAnswer(DNSRecord rec, long now) throws IOException
{
- while (true)
+ if (rec != null)
{
- int n = name.indexOf('.');
- if (n < 0)
- {
- n = name.length();
- }
- if (n <= 0)
+ // FIXME [PJYF July 27 2010] This is wrong when adding answers to a query we should not include stale records.
+ if ((now == 0) || !rec.isExpired(now))
{
- writeByte(0);
- return;
- }
- if(useCompression && USE_DOMAIN_NAME_COMPRESSION){
- Integer offset = (Integer) names.get(name);
- if (offset != null)
+ MessageOutputStream record = new MessageOutputStream(512, this);
+ record.writeRecord(rec, now);
+ byte[] byteArray = record.toByteArray();
+ if (byteArray.length < this.availableSpace())
{
- int val = offset.intValue();
-
- if (val > off)
- {
-// logger.log(Level.WARNING, "DNSOutgoing writeName failed val=" + val + " name=" + name);
- }
-
- writeByte((val >> 8) | 0xC0);
- writeByte(val & 0xFF);
- return;
+ _answers.add(rec);
+ _answersBytes.write(byteArray, 0, byteArray.length);
+ }
+ else
+ {
+ throw new IOException("message full");
}
- names.put(name, Integer.valueOf(""+off));
- }
- writeUTF(name, 0, n);
- name = name.substring(n);
- if (name.startsWith("."))
- {
- name = name.substring(1);
}
}
}
- void writeQuestion(DNSQuestion question) throws IOException
- {
- writeName(question.name);
- writeShort(question.type);
- writeShort(question.clazz);
- }
-
- void writeRecord(DNSRecord rec, long now) throws IOException
+ /**
+ * Add an authoritative answer to the message.
+ *
+ * @param rec
+ * @throws IOException
+ */
+ public void addAuthorativeAnswer(DNSRecord rec) throws IOException
{
- int save = off;
- try
+ MessageOutputStream record = new MessageOutputStream(512, this);
+ record.writeRecord(rec, 0);
+ byte[] byteArray = record.toByteArray();
+ if (byteArray.length < this.availableSpace())
{
- writeName(rec.name);
- writeShort(rec.type);
- writeShort(rec.clazz | ((rec.unique && multicast) ? DNSConstants.CLASS_UNIQUE : 0));
- writeInt((now == 0) ? rec.getTtl() : rec.getRemainingTTL(now));
- writeShort(0);
- int start = off;
- rec.write(this);
- int len = off - start;
- data[start - 2] = (byte) (len >> 8);
- data[start - 1] = (byte) (len & 0xFF);
+ _authoritativeAnswers.add(rec);
+ _authoritativeAnswersBytes.write(byteArray, 0, byteArray.length);
}
- catch (IOException e)
+ else
{
- off = save;
- throw e;
+ throw new IOException("message full");
}
}
/**
- * Finish the message before sending it off.
+ * Add an additional answer to the record. Omit if there is no room.
+ *
+ * @param in
+ * @param rec
+ * @throws IOException
*/
- void finish() throws IOException
+ public void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException
{
- int save = off;
- off = 0;
-
- writeShort(multicast ? 0 : id);
- writeShort(flags);
- writeShort(numQuestions);
- writeShort(numAnswers);
- writeShort(numAuthorities);
- writeShort(numAdditionals);
- off = save;
+ MessageOutputStream record = new MessageOutputStream(512, this);
+ record.writeRecord(rec, 0);
+ byte[] byteArray = record.toByteArray();
+ if (byteArray.length < this.availableSpace())
+ {
+ _additionals.add(rec);
+ _additionalsAnswersBytes.write(byteArray, 0, byteArray.length);
+ }
+ else
+ {
+ throw new IOException("message full");
+ }
}
- boolean isQuery()
+ /**
+ * Builds the final message buffer to be send and returns it.
+ *
+ * @return bytes to send.
+ */
+ public byte[] data()
{
- return (flags & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
+ long now = System.currentTimeMillis(); // System.currentTimeMillis()
+ _names.clear();
+
+ MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this);
+ message.writeShort(_multicast ? 0 : this.getId());
+ message.writeShort(this.getFlags());
+ message.writeShort(this.getNumberOfQuestions());
+ message.writeShort(this.getNumberOfAnswers());
+ message.writeShort(this.getNumberOfAuthorities());
+ message.writeShort(this.getNumberOfAdditionals());
+ for (DNSQuestion question : _questions)
+ {
+ message.writeQuestion(question);
+ }
+ for (DNSRecord record : _answers)
+ {
+ message.writeRecord(record, now);
+ }
+ for (DNSRecord record : _authoritativeAnswers)
+ {
+ message.writeRecord(record, now);
+ }
+ for (DNSRecord record : _additionals)
+ {
+ message.writeRecord(record, now);
+ }
+ return message.toByteArray();
}
- public boolean isEmpty()
+ @Override
+ public boolean isQuery()
{
- return numQuestions == 0 && numAuthorities == 0
- && numAdditionals == 0 && numAnswers == 0;
+ return (this.getFlags() & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
}
-
+ @Override
public String toString()
{
StringBuffer buf = new StringBuffer();
- buf.append(isQuery() ? "dns[query," : "dns[response,");
- //buf.append(packet.getAddress().getHostAddress());
- buf.append(':');
- //buf.append(packet.getPort());
- //buf.append(",len=");
- //buf.append(packet.getLength());
- buf.append(",id=0x");
- buf.append(Integer.toHexString(id));
- if (flags != 0)
- {
- buf.append(",flags=0x");
- buf.append(Integer.toHexString(flags));
- if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0)
+ buf.append(isQuery() ? "dns[query:" : "dns[response:");
+ buf.append(" id=0x");
+ buf.append(Integer.toHexString(this.getId()));
+ if (this.getFlags() != 0)
+ {
+ buf.append(", flags=0x");
+ buf.append(Integer.toHexString(this.getFlags()));
+ if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0)
{
buf.append(":r");
}
- if ((flags & DNSConstants.FLAGS_AA) != 0)
+ if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0)
{
buf.append(":aa");
}
- if ((flags & DNSConstants.FLAGS_TC) != 0)
+ if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0)
{
buf.append(":tc");
}
}
- if (numQuestions > 0)
+ if (this.getNumberOfQuestions() > 0)
{
- buf.append(",questions=");
- buf.append(numQuestions);
+ buf.append(", questions=");
+ buf.append(this.getNumberOfQuestions());
}
- if (numAnswers > 0)
+ if (this.getNumberOfAnswers() > 0)
{
- buf.append(",answers=");
- buf.append(numAnswers);
+ buf.append(", answers=");
+ buf.append(this.getNumberOfAnswers());
}
- if (numAuthorities > 0)
+ if (this.getNumberOfAuthorities() > 0)
{
- buf.append(",authorities=");
- buf.append(numAuthorities);
+ buf.append(", authorities=");
+ buf.append(this.getNumberOfAuthorities());
}
- if (numAdditionals > 0)
+ if (this.getNumberOfAdditionals() > 0)
{
- buf.append(",additionals=");
- buf.append(numAdditionals);
+ buf.append(", additionals=");
+ buf.append(this.getNumberOfAdditionals());
}
- buf.append(",\nnames=" + names);
- buf.append(",\nauthorativeAnswers=" + authorativeAnswers);
-
+ if (this.getNumberOfQuestions() > 0)
+ {
+ buf.append("\nquestions:");
+ for (DNSQuestion question : _questions)
+ {
+ buf.append("\n\t" + question);
+ }
+ }
+ if (this.getNumberOfAnswers() > 0)
+ {
+ buf.append("\nanswers:");
+ for (DNSRecord record : _answers)
+ {
+ buf.append("\n\t" + record);
+ }
+ }
+ if (this.getNumberOfAuthorities() > 0)
+ {
+ buf.append("\nauthorities:");
+ for (DNSRecord record : _authoritativeAnswers)
+ {
+ buf.append("\n\t" + record);
+ }
+ }
+ if (this.getNumberOfAdditionals() > 0)
+ {
+ buf.append("\nadditionals:");
+ for (DNSRecord record : _additionals)
+ {
+ buf.append("\n\t" + record);
+ }
+ }
+ buf.append("\nnames=" + _names);
buf.append("]");
return buf.toString();
}
+ /**
+ * @return the maxUDPPayload
+ */
+ public int getMaxUDPPayload()
+ {
+ return this._maxUDPPayload;
+ }
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSQuestion.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSQuestion.java
index 0b03da8f8..3dbd16f47 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSQuestion.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSQuestion.java
@@ -2,43 +2,356 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
package javax.jmdns.impl;
-//import java.util.logging.Logger;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
/**
* A DNS question.
*
* @version %I%, %G%
- * @author Arthur van Hoff
+ * @author Arthur van Hoff, Pierre Frisch
*/
-public final class DNSQuestion extends DNSEntry
+public class DNSQuestion extends DNSEntry
{
-// private static Logger logger = Logger.getLogger(DNSQuestion.class.getName());
+ private static Logger logger = Logger.getLogger(DNSQuestion.class.getName());
+
+ /**
+ * Address question.
+ */
+ private static class DNS4Address extends DNSQuestion
+ {
+ DNS4Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ DNSRecord answer = jmDNSImpl.getLocalHost().getDNSAddressRecord(this.getRecordType(), this.isUnique(), DNSConstants.DNS_TTL);
+ if (answer != null)
+ {
+ answers.add(answer);
+ }
+ }
+
+ @Override
+ public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl)
+ {
+ String name = this.getName().toLowerCase();
+ return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
+ }
+
+ }
+
+ /**
+ * Address question.
+ */
+ private static class DNS6Address extends DNSQuestion
+ {
+ DNS6Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ DNSRecord answer = jmDNSImpl.getLocalHost().getDNSAddressRecord(this.getRecordType(), this.isUnique(), DNSConstants.DNS_TTL);
+ if (answer != null)
+ {
+ answers.add(answer);
+ }
+ }
+
+ @Override
+ public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl)
+ {
+ String name = this.getName().toLowerCase();
+ return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
+ }
+
+ }
+
+ /**
+ * Host Information question.
+ */
+ private static class HostInformation extends DNSQuestion
+ {
+ HostInformation(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+ }
+
+ /**
+ * Pointer question.
+ */
+ private static class Pointer extends DNSQuestion
+ {
+ Pointer(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ // find matching services
+ for (ServiceInfo serviceInfo : jmDNSImpl.getServices().values())
+ {
+ this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) serviceInfo);
+ }
+ if (this.isServicesDiscoveryMetaQuery())
+ {
+ for (String serviceType : jmDNSImpl.getServiceTypes().keySet())
+ {
+ answers.add(new DNSRecord.Pointer("_services._dns-sd._udp.local.", DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, serviceType));
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Service question.
+ */
+ private static class Service extends DNSQuestion
+ {
+ Service(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ String name = this.getName().toLowerCase();
+ if (jmDNSImpl.getLocalHost().getName().equalsIgnoreCase(name))
+ {
+ // type = DNSConstants.TYPE_A;
+ answers.addAll(jmDNSImpl.getLocalHost().answers(this.isUnique(), DNSConstants.DNS_TTL));
+ return;
+ }
+ // Service type request
+ if (jmDNSImpl.getServiceTypes().containsKey(name))
+ {
+ DNSQuestion question = new Pointer(this.getName(), DNSRecordType.TYPE_PTR, this.getRecordClass(), this.isUnique());
+ question.addAnswers(jmDNSImpl, answers);
+ return;
+ }
+
+ this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) jmDNSImpl.getServices().get(name));
+ }
+
+ @Override
+ public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl)
+ {
+ String name = this.getName().toLowerCase();
+ return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
+ }
+
+ }
+
+ /**
+ * Text question.
+ */
+ private static class Text extends DNSQuestion
+ {
+ Text(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) jmDNSImpl.getServices().get(this.getName().toLowerCase()));
+ }
+
+ @Override
+ public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl)
+ {
+ String name = this.getName().toLowerCase();
+ return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
+ }
+
+ }
+
+ /**
+ * AllRecords question.
+ */
+ private static class AllRecords extends DNSQuestion
+ {
+ AllRecords(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
+
+ @Override
+ public boolean isSameType(DNSEntry entry)
+ {
+ // We match all non null entry
+ return (entry != null);
+ }
+
+ @Override
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ String name = this.getName().toLowerCase();
+ if (jmDNSImpl.getLocalHost().getName().equalsIgnoreCase(name))
+ {
+ // type = DNSConstants.TYPE_A;
+ answers.addAll(jmDNSImpl.getLocalHost().answers(this.isUnique(), DNSConstants.DNS_TTL));
+ return;
+ }
+ // Service type request
+ if (jmDNSImpl.getServiceTypes().containsKey(name))
+ {
+ DNSQuestion question = new Pointer(this.getName(), DNSRecordType.TYPE_PTR, this.getRecordClass(), this.isUnique());
+ question.addAnswers(jmDNSImpl, answers);
+ return;
+ }
+
+ this.addAnswersForServiceInfo(jmDNSImpl, answers, (ServiceInfoImpl) jmDNSImpl.getServices().get(name));
+ }
+
+ @Override
+ public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl)
+ {
+ String name = this.getName().toLowerCase();
+ return jmDNSImpl.getLocalHost().getName().equals(name) || jmDNSImpl.getServices().keySet().contains(name);
+ }
+
+ }
+
+ DNSQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
+ {
+ super(name, type, recordClass, unique);
+ }
/**
* Create a question.
+ *
+ * @param name
+ * @param type
+ * @param recordClass
+ * @param unique
+ * @return new question
*/
- public DNSQuestion(String name, int type, int clazz)
+ public static DNSQuestion newQuestion(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique)
{
- super(name, type, clazz);
+ switch (type)
+ {
+ case TYPE_A:
+ return new DNS4Address(name, type, recordClass, unique);
+ case TYPE_A6:
+ return new DNS6Address(name, type, recordClass, unique);
+ case TYPE_AAAA:
+ return new DNS6Address(name, type, recordClass, unique);
+ case TYPE_ANY:
+ return new AllRecords(name, type, recordClass, unique);
+ case TYPE_HINFO:
+ return new HostInformation(name, type, recordClass, unique);
+ case TYPE_PTR:
+ return new Pointer(name, type, recordClass, unique);
+ case TYPE_SRV:
+ return new Service(name, type, recordClass, unique);
+ case TYPE_TXT:
+ return new Text(name, type, recordClass, unique);
+ default:
+ return new DNSQuestion(name, type, recordClass, unique);
+ }
}
/**
* Check if this question is answered by a given DNS record.
*/
- boolean answeredBy(DNSRecord rec)
+ boolean answeredBy(DNSEntry rec)
+ {
+ return this.isSameRecordClass(rec) && this.isSameType(rec) && this.getName().equals(rec.getName());
+ }
+
+ /**
+ * Adds answers to the list for our question.
+ *
+ * @param jmDNSImpl
+ * DNS holding the records
+ * @param answers
+ * List of previous answer to append.
+ */
+ public void addAnswers(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers)
+ {
+ // By default we do nothing
+ }
+
+ protected void addAnswersForServiceInfo(JmDNSImpl jmDNSImpl, Set<DNSRecord> answers, ServiceInfoImpl info)
+ {
+ if ((info != null) && info.isAnnounced())
+ {
+ if (this.getName().equalsIgnoreCase(info.getQualifiedName()) || this.getName().equalsIgnoreCase(info.getType()))
+ {
+ answers.addAll(jmDNSImpl.getLocalHost().answers(this.isUnique(), DNSConstants.DNS_TTL));
+ answers.addAll(info.answers(this.isUnique(), DNSConstants.DNS_TTL, jmDNSImpl.getLocalHost()));
+ }
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer(jmDNSImpl.getName() + " DNSQuestion(" + this.getName() + ").addAnswersForServiceInfo(): info: " + info + "\n" + answers);
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#isStale(long)
+ */
+ @Override
+ public boolean isStale(long now)
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#isExpired(long)
+ */
+ @Override
+ public boolean isExpired(long now)
{
- return (clazz == rec.clazz) && ((type == rec.type) || (type == DNSConstants.TYPE_ANY)) &&
- name.equals(rec.name);
+ return false;
}
/**
- * For debugging only.
+ * Checks if we are the only to be able to answer that question.
+ *
+ * @param jmDNSImpl
+ * DNS holding the records
+ * @return <code>true</code> if we are the only one with the answer to the question, <code>false</code> otherwise.
*/
- public String toString()
+ public boolean iAmTheOnlyOne(JmDNSImpl jmDNSImpl)
{
- return toString("question", null);
+ return false;
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#toString(java.lang.StringBuilder)
+ */
+ @Override
+ public void toString(StringBuilder aLog)
+ {
+ // do nothing
+ }
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSRecord.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSRecord.java
index c50992b7c..77b0077f6 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSRecord.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSRecord.java
@@ -2,61 +2,64 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
package javax.jmdns.impl;
-import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.util.Iterator;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.ServiceEvent;
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.ServiceInfo.Fields;
+import javax.jmdns.impl.DNSOutgoing.MessageOutputStream;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
/**
* DNS record
*
* @version %I%, %G%
- * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch
+ * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch
*/
public abstract class DNSRecord extends DNSEntry
{
-// private static Logger logger = Logger.getLogger(DNSRecord.class.getName());
- private int ttl;
- private long created;
-
- /**
- * This source is mainly for debugging purposes, should be the address that
- * sent this record.
- */
- private InetAddress source;
+ private static Logger logger = Logger.getLogger(DNSRecord.class.getName());
+ private int _ttl;
+ private long _created;
/**
- * Create a DNSRecord with a name, type, clazz, and ttl.
+ * This source is mainly for debugging purposes, should be the address that sent this record.
*/
- DNSRecord(String name, int type, int clazz, int ttl)
- {
- super(name, type, clazz);
- this.ttl = ttl;
- this.created = System.currentTimeMillis();
- }
+ private InetAddress _source;
/**
- * True if this record is the same as some other record.
+ * Create a DNSRecord with a name, type, class, and ttl.
*/
- public boolean equals(Object other)
+ DNSRecord(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl)
{
- return (other instanceof DNSRecord) && sameAs((DNSRecord) other);
+ super(name, type, recordClass, unique);
+ this._ttl = ttl;
+ this._created = System.currentTimeMillis();
}
- /**
- * True if this record is the same as some other record.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#equals(java.lang.Object)
*/
- boolean sameAs(DNSRecord other)
+ @Override
+ public boolean equals(Object other)
{
- return super.equals(other) && sameValue((DNSRecord) other);
+ return (other instanceof DNSRecord) && super.equals(other) && sameValue((DNSRecord) other);
}
/**
@@ -69,22 +72,20 @@ public abstract class DNSRecord extends DNSEntry
*/
boolean sameType(DNSRecord other)
{
- return type == other.type;
+ return this.getRecordType() == other.getRecordType();
}
/**
* Handles a query represented by this record.
*
- * @return Returns true if a conflict with one of the services registered
- * with JmDNS or with the hostname occured.
+ * @return Returns true if a conflict with one of the services registered with JmDNS or with the hostname occured.
*/
abstract boolean handleQuery(JmDNSImpl dns, long expirationTime);
/**
- * Handles a responserepresented by this record.
+ * Handles a response represented by this record.
*
- * @return Returns true if a conflict with one of the services registered
- * with JmDNS or with the hostname occured.
+ * @return Returns true if a conflict with one of the services registered with JmDNS or with the hostname occured.
*/
abstract boolean handleResponse(JmDNSImpl dns);
@@ -100,9 +101,9 @@ public abstract class DNSRecord extends DNSEntry
{
try
{
- for (int i = msg.numAnswers; i-- > 0;)
+ for (DNSRecord answer : msg.getAllAnswers())
{
- if (suppressedBy((DNSRecord) msg.answers.get(i)))
+ if (suppressedBy(answer))
{
return true;
}
@@ -111,20 +112,18 @@ public abstract class DNSRecord extends DNSEntry
}
catch (ArrayIndexOutOfBoundsException e)
{
-// logger.log(Level.WARNING, "suppressedBy() message " + msg + " exception ", e);
+ logger.log(Level.WARNING, "suppressedBy() message " + msg + " exception ", e);
// msg.print(true);
return false;
}
}
/**
- * True if this record would be supressed by an answer.
- * This is the case if this record would not have a
- * significantly longer TTL.
+ * True if this record would be suppressed by an answer. This is the case if this record would not have a significantly longer TTL.
*/
boolean suppressedBy(DNSRecord other)
{
- if (sameAs(other) && (other.ttl > ttl / 2))
+ if (this.equals(other) && (other._ttl > _ttl / 2))
{
return true;
}
@@ -136,7 +135,7 @@ public abstract class DNSRecord extends DNSEntry
*/
long getExpirationTime(int percent)
{
- return created + (percent * ttl * 10L);
+ return _created + (percent * _ttl * 10L);
}
/**
@@ -147,206 +146,235 @@ public abstract class DNSRecord extends DNSEntry
return (int) Math.max(0, (getExpirationTime(100) - now) / 1000);
}
- /**
- * Check if the record is expired.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#isExpired(long)
*/
+ @Override
public boolean isExpired(long now)
{
return getExpirationTime(100) <= now;
}
- /**
- * Check if the record is stale, ie it has outlived
- * more than half of its TTL.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#isStale(long)
*/
- boolean isStale(long now)
+ @Override
+ public boolean isStale(long now)
{
return getExpirationTime(50) <= now;
}
/**
- * Reset the TTL of a record. This avoids having to
- * update the entire record in the cache.
+ * Reset the TTL of a record. This avoids having to update the entire record in the cache.
*/
void resetTTL(DNSRecord other)
{
- created = other.created;
- ttl = other.ttl;
+ _created = other._created;
+ _ttl = other._ttl;
}
/**
* Write this record into an outgoing message.
*/
- abstract void write(DNSOutgoing out) throws IOException;
+ abstract void write(MessageOutputStream out);
- /**
- * Address record.
- */
- static class Address extends DNSRecord
+ public static class IPv4Address extends Address
{
-// private static Logger logger = Logger.getLogger(Address.class.getName());
- InetAddress addr;
- Address(String name, int type, int clazz, int ttl, InetAddress addr)
+ IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr)
{
- super(name, type, clazz, ttl);
- this.addr = addr;
+ super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, addr);
}
- Address(String name, int type, int clazz, int ttl, byte[] rawAddress)
+ IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress)
{
- super(name, type, clazz, ttl);
- try
- {
- this.addr = InetAddress.getByAddress(rawAddress);
- }
- catch (UnknownHostException exception)
- {
-// logger.log(Level.WARNING, "Address() exception ", exception);
- }
+ super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, rawAddress);
}
- void write(DNSOutgoing out) throws IOException
+ @Override
+ void write(MessageOutputStream out)
{
- if (addr != null)
+ if (_addr != null)
{
- byte[] buffer = addr.getAddress();
- if (DNSConstants.TYPE_A == type)
+ byte[] buffer = _addr.getAddress();
+ // If we have a type A records we should answer with a IPv4 address
+ if (_addr instanceof Inet4Address)
{
- // If we have a type A records we should answer with a IPv4 address
- if (addr instanceof Inet4Address)
- {
- // All is good
- }
- else
- {
- // Get the last four bytes
- byte[] tempbuffer = buffer;
- buffer = new byte[4];
- System.arraycopy(tempbuffer, 12, buffer, 0, 4);
- }
+ // All is good
}
else
{
- // If we have a type AAAA records we should answer with a IPv6 address
- if (addr instanceof Inet4Address)
- {
- byte[] tempbuffer = buffer;
- buffer = new byte[16];
- for (int i = 0; i < 16; i++)
- {
- if (i < 11)
- {
- buffer[i] = tempbuffer[i - 12];
- }
- else
- {
- buffer[i] = 0;
- }
- }
- }
+ // Get the last four bytes
+ byte[] tempbuffer = buffer;
+ buffer = new byte[4];
+ System.arraycopy(tempbuffer, 12, buffer, 0, 4);
}
int length = buffer.length;
out.writeBytes(buffer, 0, length);
}
}
- boolean same(DNSRecord other)
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
{
- return ((sameName(other)) && ((sameValue(other))));
+
+ ServiceInfoImpl info = (ServiceInfoImpl) super.getServiceInfo(persistent);
+ info.setAddress((Inet4Address) _addr);
+ return info;
}
- boolean sameName(DNSRecord other)
+ }
+
+ public static class IPv6Address extends Address
+ {
+
+ IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr)
{
- return name.equalsIgnoreCase(((Address) other).name);
+ super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, addr);
}
- boolean sameValue(DNSRecord other)
+ IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress)
{
- return addr.equals(((Address) other).getAddress());
+ super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, rawAddress);
}
- InetAddress getAddress()
+ @Override
+ void write(MessageOutputStream out)
{
- return addr;
+ if (_addr != null)
+ {
+ byte[] buffer = _addr.getAddress();
+ // If we have a type AAAA records we should answer with a IPv6 address
+ if (_addr instanceof Inet4Address)
+ {
+ byte[] tempbuffer = buffer;
+ buffer = new byte[16];
+ for (int i = 0; i < 16; i++)
+ {
+ if (i < 11)
+ {
+ buffer[i] = tempbuffer[i - 12];
+ }
+ else
+ {
+ buffer[i] = 0;
+ }
+ }
+ }
+ int length = buffer.length;
+ out.writeBytes(buffer, 0, length);
+ }
}
- /**
- * Creates a byte array representation of this record.
- * This is needed for tie-break tests according to
- * draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
*/
- private byte[] toByteArray()
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
+ {
+
+ ServiceInfoImpl info = (ServiceInfoImpl) super.getServiceInfo(persistent);
+ info.setAddress((Inet6Address) _addr);
+ return info;
+ }
+
+ }
+
+ /**
+ * Address record.
+ */
+ public static abstract class Address extends DNSRecord
+ {
+ private static Logger logger1 = Logger.getLogger(Address.class.getName());
+
+ InetAddress _addr;
+
+ protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr)
+ {
+ super(name, type, recordClass, unique, ttl);
+ this._addr = addr;
+ }
+
+ protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress)
{
+ super(name, type, recordClass, unique, ttl);
try
{
- ByteArrayOutputStream bout = new ByteArrayOutputStream();
- DataOutputStream dout = new DataOutputStream(bout);
- dout.write(name.getBytes("UTF8"));
- dout.writeShort(type);
- dout.writeShort(clazz);
- //dout.writeInt(len);
- byte[] buffer = addr.getAddress();
- for (int i = 0; i < buffer.length; i++)
- {
- dout.writeByte(buffer[i]);
- }
- dout.close();
- return bout.toByteArray();
+ this._addr = InetAddress.getByAddress(rawAddress);
}
- catch (IOException e)
+ catch (UnknownHostException exception)
{
- throw new InternalError();
+ logger1.log(Level.WARNING, "Address() exception ", exception);
}
}
+ boolean same(DNSRecord other)
+ {
+ return ((sameName(other)) && ((sameValue(other))));
+ }
+
+ boolean sameName(DNSRecord other)
+ {
+ return this.getName().equalsIgnoreCase(((Address) other).getName());
+ }
+
+ @Override
+ boolean sameValue(DNSRecord other)
+ {
+ return this.getAddress().equals(((Address) other).getAddress());
+ }
+
+ InetAddress getAddress()
+ {
+ return _addr;
+ }
+
/**
- * Does a lexicographic comparison of the byte array representation
- * of this record and that record.
- * This is needed for tie-break tests according to
- * draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
+ * Creates a byte array representation of this record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
*/
- private int lexCompare(DNSRecord.Address that)
+ @Override
+ protected void toByteArray(DataOutputStream dout) throws IOException
{
- byte[] thisBytes = this.toByteArray();
- byte[] thatBytes = that.toByteArray();
- for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); i < n; i++)
+ super.toByteArray(dout);
+ byte[] buffer = this.getAddress().getAddress();
+ for (int i = 0; i < buffer.length; i++)
{
- if (thisBytes[i] > thatBytes[i])
- {
- return 1;
- }
- else
- {
- if (thisBytes[i] < thatBytes[i])
- {
- return -1;
- }
- }
+ dout.writeByte(buffer[i]);
}
- return thisBytes.length - thatBytes.length;
}
/**
* Does the necessary actions, when this as a query.
*/
+ @Override
boolean handleQuery(JmDNSImpl dns, long expirationTime)
{
- DNSRecord.Address dnsAddress = dns.getLocalHost().getDNSAddressRecord(this);
+ DNSRecord.Address dnsAddress = dns.getLocalHost().getDNSAddressRecord(this.getRecordType(), this.isUnique(), DNSConstants.DNS_TTL);
if (dnsAddress != null)
{
if (dnsAddress.sameType(this) && dnsAddress.sameName(this) && (!dnsAddress.sameValue(this)))
{
-// logger.finer("handleQuery() Conflicting probe detected. dns state " + dns.getState() + " lex compare " + lexCompare(dnsAddress));
+ logger1.finer("handleQuery() Conflicting probe detected. lex compare " + compareTo(dnsAddress));
// Tie-breaker test
- if (dns.getState().isProbing() && lexCompare(dnsAddress) >= 0)
+ if (dns.isProbing() && compareTo(dnsAddress) >= 0)
{
// We lost the tie-break. We have to choose a different name.
dns.getLocalHost().incrementHostName();
dns.getCache().clear();
- for (Iterator i = dns.getServices().values().iterator(); i.hasNext();)
+ for (ServiceInfo serviceInfo : dns.getServices().values())
{
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
+ ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo;
info.revertState();
}
}
@@ -360,22 +388,23 @@ public abstract class DNSRecord extends DNSEntry
/**
* Does the necessary actions, when this as a response.
*/
+ @Override
boolean handleResponse(JmDNSImpl dns)
{
- DNSRecord.Address dnsAddress = dns.getLocalHost().getDNSAddressRecord(this);
+ DNSRecord.Address dnsAddress = dns.getLocalHost().getDNSAddressRecord(this.getRecordType(), this.isUnique(), DNSConstants.DNS_TTL);
if (dnsAddress != null)
{
if (dnsAddress.sameType(this) && dnsAddress.sameName(this) && (!dnsAddress.sameValue(this)))
{
-// logger.finer("handleResponse() Denial detected");
+ logger1.finer("handleResponse() Denial detected");
- if (dns.getState().isProbing())
+ if (dns.isProbing())
{
dns.getLocalHost().incrementHostName();
dns.getCache().clear();
- for (Iterator i = dns.getServices().values().iterator(); i.hasNext();)
+ for (ServiceInfo serviceInfo : dns.getServices().values())
{
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
+ ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo;
info.revertState();
}
}
@@ -386,14 +415,48 @@ public abstract class DNSRecord extends DNSEntry
return false;
}
+ @Override
DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
return out;
}
- public String toString()
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
+ {
+ ServiceInfoImpl info = new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null);
+ // info.setAddress(_addr); This is done in the sub class so we don't have to test for class type
+ return info;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
+ */
+ @Override
+ public ServiceEvent getServiceEvent(JmDNSImpl dns)
+ {
+ ServiceInfo info = this.getServiceInfo(false);
+ ((ServiceInfoImpl) info).setDns(dns);
+ return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
+ */
+ @Override
+ protected void toString(StringBuilder aLog)
{
- return toString(" address '" + (addr != null ? addr.getHostAddress() : "null") + "'");
+ super.toString(aLog);
+ aLog.append(" address: '" + (this.getAddress() != null ? this.getAddress().getHostAddress() : "null") + "'");
}
}
@@ -403,25 +466,39 @@ public abstract class DNSRecord extends DNSEntry
*/
public static class Pointer extends DNSRecord
{
-// private static Logger logger = Logger.getLogger(Pointer.class.getName());
- String alias;
+ // private static Logger logger = Logger.getLogger(Pointer.class.getName());
+ String _alias;
+
+ public Pointer(String name, DNSRecordClass recordClass, boolean unique, int ttl, String alias)
+ {
+ super(name, DNSRecordType.TYPE_PTR, recordClass, unique, ttl);
+ this._alias = alias;
+ }
- public Pointer(String name, int type, int clazz, int ttl, String alias)
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSEntry#isSameEntry(javax.jmdns.impl.DNSEntry)
+ */
+ @Override
+ public boolean isSameEntry(DNSEntry entry)
{
- super(name, type, clazz, ttl);
- this.alias = alias;
+ return super.isSameEntry(entry) && (entry instanceof Pointer) && this.sameValue((Pointer) entry);
}
- void write(DNSOutgoing out) throws IOException
+ @Override
+ void write(MessageOutputStream out)
{
- out.writeName(alias);
+ out.writeName(_alias, false);
}
+ @Override
boolean sameValue(DNSRecord other)
{
- return alias.equals(((Pointer) other).alias);
+ return _alias.equals(((Pointer) other)._alias);
}
+ @Override
boolean handleQuery(JmDNSImpl dns, long expirationTime)
{
// Nothing to do (?)
@@ -429,6 +506,7 @@ public abstract class DNSRecord extends DNSEntry
return false;
}
+ @Override
boolean handleResponse(JmDNSImpl dns)
{
// Nothing to do (?)
@@ -438,46 +516,100 @@ public abstract class DNSRecord extends DNSEntry
String getAlias()
{
- return alias;
+ return _alias;
}
+ @Override
DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
return out;
}
- public String toString()
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
{
- return toString(alias);
+ if (this.isServicesDiscoveryMetaQuery())
+ {
+ // The service name is in the alias
+ Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getAlias());
+ return new ServiceInfoImpl(map, 0, 0, 0, persistent, (byte[]) null);
+ }
+ else if (this.isDomainDiscoveryQuery())
+ {
+ // FIXME [PJYF July 3 2010] Not sure what to do with this at this point
+ return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null);
+ }
+ else if (this.isReverseLookup())
+ {
+ return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null);
+ }
+ Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getAlias());
+ map.put(Fields.Subtype, this.getQualifiedNameMap().get(Fields.Subtype));
+ return new ServiceInfoImpl(map, 0, 0, 0, persistent, this.getAlias());
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
+ */
+ @Override
+ public ServiceEvent getServiceEvent(JmDNSImpl dns)
+ {
+ ServiceInfo info = this.getServiceInfo(false);
+ ((ServiceInfoImpl) info).setDns(dns);
+ String domainName = info.getType();
+ String serviceName = JmDNSImpl.toUnqualifiedName(domainName, this.getAlias());
+ return new ServiceEventImpl(dns, domainName, serviceName, info);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
+ */
+ @Override
+ protected void toString(StringBuilder aLog)
+ {
+ super.toString(aLog);
+ aLog.append(" alias: '" + (_alias != null ? _alias.toString() : "null") + "'");
+ }
+
}
public static class Text extends DNSRecord
{
-// private static Logger logger = Logger.getLogger(Text.class.getName());
- byte text[];
+ // private static Logger logger = Logger.getLogger(Text.class.getName());
+ byte[] _text;
- public Text(String name, int type, int clazz, int ttl, byte text[])
+ public Text(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte text[])
{
- super(name, type, clazz, ttl);
- this.text = text;
+ super(name, DNSRecordType.TYPE_TXT, recordClass, unique, ttl);
+ this._text = text;
}
- void write(DNSOutgoing out) throws IOException
+ @Override
+ void write(MessageOutputStream out)
{
- out.writeBytes(text, 0, text.length);
+ out.writeBytes(_text, 0, _text.length);
}
+ @Override
boolean sameValue(DNSRecord other)
{
Text txt = (Text) other;
- if (txt.text.length != text.length)
+ if (txt._text.length != _text.length)
{
return false;
}
- for (int i = text.length; i-- > 0;)
+ for (int i = _text.length; i-- > 0;)
{
- if (txt.text[i] != text[i])
+ if (txt._text[i] != _text[i])
{
return false;
}
@@ -485,6 +617,7 @@ public abstract class DNSRecord extends DNSEntry
return true;
}
+ @Override
boolean handleQuery(JmDNSImpl dns, long expirationTime)
{
// Nothing to do (?)
@@ -492,30 +625,59 @@ public abstract class DNSRecord extends DNSEntry
return false;
}
+ @Override
boolean handleResponse(JmDNSImpl dns)
{
// Nothing to do (?)
// Shouldn't we care if we get a conflict at this level?
/*
- ServiceInfo info = (ServiceInfo) dns.services.get(name.toLowerCase());
- if (info != null) {
- if (! Arrays.equals(text,info.text)) {
- info.revertState();
- return true;
- }
- }*/
+ * ServiceInfo info = (ServiceInfo) dns.services.get(name.toLowerCase()); if (info != null) { if (! Arrays.equals(text,info.text)) { info.revertState(); return true; } }
+ */
return false;
}
+ @Override
DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
return out;
}
- public String toString()
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
{
- return toString((text.length > 10) ? new String(text, 0, 7) + "..." : new String(text));
+ return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, _text);
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
+ */
+ @Override
+ public ServiceEvent getServiceEvent(JmDNSImpl dns)
+ {
+ ServiceInfo info = this.getServiceInfo(false);
+ ((ServiceInfoImpl) info).setDns(dns);
+ return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
+ */
+ @Override
+ protected void toString(StringBuilder aLog)
+ {
+ super.toString(aLog);
+ aLog.append(" text: '" + ((_text.length > 20) ? new String(_text, 0, 17) + "..." : new String(_text)) + "'");
+ }
+
}
/**
@@ -523,166 +685,169 @@ public abstract class DNSRecord extends DNSEntry
*/
public static class Service extends DNSRecord
{
-// private static Logger logger = Logger.getLogger(Service.class.getName());
- int priority;
- int weight;
- int port;
- String server;
+ private static Logger logger1 = Logger.getLogger(Service.class.getName());
+ private int _priority;
+ private int _weight;
+ private int _port;
+ private String _server;
- public Service(String name, int type, int clazz, int ttl, int priority, int weight, int port, String server)
+ public Service(String name, DNSRecordClass recordClass, boolean unique, int ttl, int priority, int weight, int port, String server)
{
- super(name, type, clazz, ttl);
- this.priority = priority;
- this.weight = weight;
- this.port = port;
- this.server = server;
+ super(name, DNSRecordType.TYPE_SRV, recordClass, unique, ttl);
+ this._priority = priority;
+ this._weight = weight;
+ this._port = port;
+ this._server = server;
}
- void write(DNSOutgoing out) throws IOException
+ @Override
+ void write(MessageOutputStream out)
{
- out.writeShort(priority);
- out.writeShort(weight);
- out.writeShort(port);
- if(DNSIncoming.USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET){
- out.writeName(server, false);
- } else {
- out.writeUTF(server, 0, server.length());
+ out.writeShort(_priority);
+ out.writeShort(_weight);
+ out.writeShort(_port);
+ if (DNSIncoming.USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET)
+ {
+ out.writeName(_server, false);
+ }
+ else
+ {
+ out.writeUTF(_server, 0, _server.length());
- // add a zero byte to the end just to be safe, this is the strange form
+ // add a zero byte to the end just to be safe, this is the strange form
// used by the BonjourConformanceTest
out.writeByte(0);
}
}
- private byte[] toByteArray()
+ @Override
+ protected void toByteArray(DataOutputStream dout) throws IOException
{
+ super.toByteArray(dout);
+ dout.writeShort(_priority);
+ dout.writeShort(_weight);
+ dout.writeShort(_port);
try
{
- ByteArrayOutputStream bout = new ByteArrayOutputStream();
- DataOutputStream dout = new DataOutputStream(bout);
- dout.write(name.getBytes("UTF8"));
- dout.writeShort(type);
- dout.writeShort(clazz);
- //dout.writeInt(len);
- dout.writeShort(priority);
- dout.writeShort(weight);
- dout.writeShort(port);
- dout.write(server.getBytes("UTF8"));
- dout.close();
- return bout.toByteArray();
+ dout.write(_server.getBytes("UTF-8"));
}
- catch (IOException e)
+ catch (UnsupportedEncodingException exception)
{
- throw new InternalError();
+ /* UTF-8 is always present */
}
}
- private int lexCompare(DNSRecord.Service that)
+ String getServer()
{
- byte[] thisBytes = this.toByteArray();
- byte[] thatBytes = that.toByteArray();
- for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); i < n; i++)
- {
- if (thisBytes[i] > thatBytes[i])
- {
- return 1;
- }
- else
- {
- if (thisBytes[i] < thatBytes[i])
- {
- return -1;
- }
- }
- }
- return thisBytes.length - thatBytes.length;
+ return _server;
+ }
+
+ /**
+ * @return the priority
+ */
+ public int getPriority()
+ {
+ return this._priority;
+ }
+
+ /**
+ * @return the weight
+ */
+ public int getWeight()
+ {
+ return this._weight;
}
+ /**
+ * @return the port
+ */
+ public int getPort()
+ {
+ return this._port;
+ }
+
+ @Override
boolean sameValue(DNSRecord other)
{
Service s = (Service) other;
- return (priority == s.priority) && (weight == s.weight) && (port == s.port) && server.equals(s.server);
+ return (_priority == s._priority) && (_weight == s._weight) && (_port == s._port) && _server.equals(s._server);
}
+ @Override
boolean handleQuery(JmDNSImpl dns, long expirationTime)
{
- ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(name.toLowerCase());
- if (info != null
- && (port != info.port || !server.equalsIgnoreCase(dns.getLocalHost().getName())))
+ ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey());
+ if (info != null && (_port != info.getPort() || !_server.equalsIgnoreCase(dns.getLocalHost().getName())))
{
-// logger.finer("handleQuery() Conflicting probe detected from: " + getRecordSource());
- DNSRecord.Service localService = new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV,
- DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE,
- DNSConstants.DNS_TTL, info.priority,
- info.weight, info.port, dns.getLocalHost().getName());
-
- // This block is useful for debugging race conditions when jmdns is respoding to
- // itself.
+ logger1.finer("handleQuery() Conflicting probe detected from: " + getRecordSource());
+ DNSRecord.Service localService = new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(), dns.getLocalHost().getName());
+
+ // This block is useful for debugging race conditions when jmdns is responding to itself.
try
{
- if(dns.getInterface().equals(getRecordSource())){
-// logger.warning("Got conflicting probe from ourselves\n" +
-// "incoming: " +this.toString() + "\n" +
-// "local : " + localService.toString());
+ if (dns.getInterface().equals(getRecordSource()))
+ {
+ logger1.warning("Got conflicting probe from ourselves\n" + "incoming: " + this.toString() + "\n" + "local : " + localService.toString());
}
}
catch (IOException e)
{
e.printStackTrace();
}
-
- int comparison = lexCompare(localService);
-
- if(comparison == 0){
+
+ int comparison = this.compareTo(localService);
+
+ if (comparison == 0)
+ {
// the 2 records are identical this probably means we are seeing our own record.
- // With mutliple interfaces on a single computer it is possible to see our
- // own records come in on different interfaces than the ones they were sent on.
+ // With multiple interfaces on a single computer it is possible to see our
+ // own records come in on different interfaces than the ones they were sent on.
// see section "10. Conflict Resolution" of mdns draft spec.
-// logger.finer("handleQuery() Ignoring a identical service query");
+ logger1.finer("handleQuery() Ignoring a identical service query");
return false;
}
-
+
// Tie breaker test
- if (info.getState().isProbing() && comparison > 0)
+ if (info.isProbing() && comparison > 0)
{
// We lost the tie break
String oldName = info.getQualifiedName().toLowerCase();
info.setName(dns.incrementName(info.getName()));
dns.getServices().remove(oldName);
dns.getServices().put(info.getQualifiedName().toLowerCase(), info);
-// logger.finer("handleQuery() Lost tie break: new unique name chosen:" + info.getName());
+ logger1.finer("handleQuery() Lost tie break: new unique name chosen:" + info.getName());
// We revert the state to start probing again with the new name
info.revertState();
- }
- else
+ }
+ else
{
// We won the tie break, so this conflicting probe should be ignored
// See paragraph 3 of section 9.2 in mdns draft spec
return false;
}
-
+
return true;
}
return false;
}
+ @Override
boolean handleResponse(JmDNSImpl dns)
{
- ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(name.toLowerCase());
- if (info != null
- && (port != info.port || !server.equalsIgnoreCase(dns.getLocalHost().getName())))
+ ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey());
+ if (info != null && (_port != info.getPort() || !_server.equalsIgnoreCase(dns.getLocalHost().getName())))
{
-// logger.finer("handleResponse() Denial detected");
+ logger1.finer("handleResponse() Denial detected");
- if (info.getState().isProbing())
+ if (info.isProbing())
{
String oldName = info.getQualifiedName().toLowerCase();
info.setName(dns.incrementName(info.getName()));
dns.getServices().remove(oldName);
dns.getServices().put(info.getQualifiedName().toLowerCase(), info);
-// logger.finer("handleResponse() New unique name chose:" + info.getName());
+ logger1.finer("handleResponse() New unique name chose:" + info.getName());
}
info.revertState();
@@ -691,52 +856,247 @@ public abstract class DNSRecord extends DNSEntry
return false;
}
+ @Override
DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
- ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(name.toLowerCase());
+ ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey());
if (info != null)
{
- if (this.port == info.port != server.equals(dns.getLocalHost().getName()))
+ if (this._port == info.getPort() != _server.equals(dns.getLocalHost().getName()))
{
- return dns.addAnswer(in, addr, port, out,
- new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV,
- DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE,
- DNSConstants.DNS_TTL, info.priority,
- info.weight, info.port, dns.getLocalHost().getName()));
+ return dns.addAnswer(in, addr, port, out, new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(), dns
+ .getLocalHost().getName()));
}
}
return out;
}
- public String toString()
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
{
- return toString(server + ":" + port);
+ return new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, persistent, _server);
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
+ */
+ @Override
+ public ServiceEvent getServiceEvent(JmDNSImpl dns)
+ {
+ ServiceInfo info = this.getServiceInfo(false);
+ ((ServiceInfoImpl) info).setDns(dns);
+ // String domainName = "";
+ // String serviceName = this.getServer();
+ // int index = serviceName.indexOf('.');
+ // if (index > 0)
+ // {
+ // serviceName = this.getServer().substring(0, index);
+ // if (index + 1 < this.getServer().length())
+ // domainName = this.getServer().substring(index + 1);
+ // }
+ // return new ServiceEventImpl(dns, domainName, serviceName, info);
+ return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
+ */
+ @Override
+ protected void toString(StringBuilder aLog)
+ {
+ super.toString(aLog);
+ aLog.append(" server: '" + _server + ":" + _port + "'");
+ }
+
}
+ public static class HostInformation extends DNSRecord
+ {
+ String _os;
+ String _cpu;
+
+ /**
+ * @param name
+ * @param recordClass
+ * @param unique
+ * @param ttl
+ * @param cpu
+ * @param os
+ */
+ public HostInformation(String name, DNSRecordClass recordClass, boolean unique, int ttl, String cpu, String os)
+ {
+ super(name, DNSRecordType.TYPE_HINFO, recordClass, unique, ttl);
+ _cpu = cpu;
+ _os = os;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#addAnswer(javax.jmdns.impl.JmDNSImpl, javax.jmdns.impl.DNSIncoming, java.net.InetAddress, int, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
+ {
+ return out;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#handleQuery(javax.jmdns.impl.JmDNSImpl, long)
+ */
+ @Override
+ boolean handleQuery(JmDNSImpl dns, long expirationTime)
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#handleResponse(javax.jmdns.impl.JmDNSImpl)
+ */
+ @Override
+ boolean handleResponse(JmDNSImpl dns)
+ {
+ return false;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#sameValue(javax.jmdns.impl.DNSRecord)
+ */
+ @Override
+ boolean sameValue(DNSRecord other)
+ {
+ HostInformation hinfo = (HostInformation) other;
+ return _cpu.equals(hinfo._cpu) && _os.equals(hinfo._os);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#write(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ void write(MessageOutputStream out)
+ {
+ String hostInfo = _cpu + " " + _os;
+ out.writeUTF(hostInfo, 0, hostInfo.length());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(boolean persistent)
+ {
+ Map<String, String> hinfo = new HashMap<String, String>(2);
+ hinfo.put("cpu", _cpu);
+ hinfo.put("os", _os);
+ return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, hinfo);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
+ */
+ @Override
+ public ServiceEvent getServiceEvent(JmDNSImpl dns)
+ {
+ ServiceInfo info = this.getServiceInfo(false);
+ ((ServiceInfoImpl) info).setDns(dns);
+ return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
+ */
+ @Override
+ protected void toString(StringBuilder aLog)
+ {
+ super.toString(aLog);
+ aLog.append(" cpu: '" + _cpu + "' os: '" + _os + "'");
+ }
+
+ }
+
+ /**
+ *
+ * Return a service information associated with that record if appropriate.
+ *
+ * @return service information
+ */
+ public ServiceInfo getServiceInfo()
+ {
+ return this.getServiceInfo(false);
+ }
+
+ /**
+ *
+ * Return a service information associated with that record if appropriate.
+ *
+ * @param persistent
+ * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
+ * @return service information
+ */
+ public abstract ServiceInfo getServiceInfo(boolean persistent);
+
+ /**
+ * Creates and return a service event for this record.
+ *
+ * @param dns
+ * DNS serviced by this event
+ * @return service event
+ */
+ public abstract ServiceEvent getServiceEvent(JmDNSImpl dns);
+
public void setRecordSource(InetAddress source)
{
- this.source = source;
+ this._source = source;
}
-
+
public InetAddress getRecordSource()
{
- return source;
+ return _source;
}
-
- public String toString(String other)
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
+ */
+ @Override
+ protected void toString(StringBuilder aLog)
{
- return toString("record", ttl + "/" + getRemainingTTL(System.currentTimeMillis()) + "," + other);
+ super.toString(aLog);
+ aLog.append(" ttl: '" + getRemainingTTL(System.currentTimeMillis()) + "/" + _ttl + "'");
}
- public void setTtl(int ttl)
+ public void setTTL(int ttl)
{
- this.ttl = ttl;
+ this._ttl = ttl;
}
- public int getTtl()
+ public int getTTL()
{
- return ttl;
+ return _ttl;
}
}
-
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSState.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSState.java
deleted file mode 100644
index bd941a721..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSState.java
+++ /dev/null
@@ -1,111 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl;
-
-import java.util.ArrayList;
-//import java.util.logging.Logger;
-
-/**
- * DNSState defines the possible states for services registered with JmDNS.
- *
- * @author Werner Randelshofer, Rick Blair
- * @version 1.0 May 23, 2004 Created.
- */
-public class DNSState implements Comparable
-{
-// private static Logger logger = Logger.getLogger(DNSState.class.getName());
-
- private final String name;
-
- /**
- * Ordinal of next state to be created.
- */
- private static int nextOrdinal = 0;
- /**
- * Assign an ordinal to this state.
- */
- private final int ordinal = nextOrdinal++;
- /**
- * Logical sequence of states.
- * The sequence is consistent with the ordinal of a state.
- * This is used for advancing through states.
- */
- private final static ArrayList sequence = new ArrayList();
-
- private DNSState(String name)
- {
- this.name = name;
- sequence.add(this);
- }
-
- public final String toString()
- {
- return name;
- }
-
- public static final DNSState PROBING_1 = new DNSState("probing 1");
- public static final DNSState PROBING_2 = new DNSState("probing 2");
- public static final DNSState PROBING_3 = new DNSState("probing 3");
- public static final DNSState ANNOUNCING_1 = new DNSState("announcing 1");
- public static final DNSState ANNOUNCING_2 = new DNSState("announcing 2");
- public static final DNSState ANNOUNCED = new DNSState("announced");
- public static final DNSState CANCELED = new DNSState("canceled");
-
- /**
- * Returns the next advanced state.
- * In general, this advances one step in the following sequence: PROBING_1,
- * PROBING_2, PROBING_3, ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED.
- * Does not advance for ANNOUNCED and CANCELED state.
- */
- public final DNSState advance()
- {
- return (isProbing() || isAnnouncing()) ? (DNSState) sequence.get(ordinal + 1) : this;
- }
-
- /**
- * Returns to the next reverted state.
- * All states except CANCELED revert to PROBING_1.
- * Status CANCELED does not revert.
- */
- public final DNSState revert()
- {
- return (this == CANCELED) ? this : PROBING_1;
- }
-
- /**
- * Returns true, if this is a probing state.
- */
- public boolean isProbing()
- {
- return compareTo(PROBING_1) >= 0 && compareTo(PROBING_3) <= 0;
- }
-
- /**
- * Returns true, if this is an announcing state.
- */
- public boolean isAnnouncing()
- {
- return compareTo(ANNOUNCING_1) >= 0 && compareTo(ANNOUNCING_2) <= 0;
- }
-
- /**
- * Returns true, if this is an announced state.
- */
- public boolean isAnnounced()
- {
- return compareTo(ANNOUNCED) == 0;
- }
-
- /**
- * Compares two states.
- * The states compare as follows:
- * PROBING_1 &lt; PROBING_2 &lt; PROBING_3 &lt; ANNOUNCING_1 &lt;
- * ANNOUNCING_2 &lt; RESPONDING &lt; ANNOUNCED &lt; CANCELED.
- */
- public int compareTo(Object o)
- {
- return ordinal - ((DNSState) o).ordinal;
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSStatefulObject.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSStatefulObject.java
new file mode 100644
index 000000000..b207c15f2
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/DNSStatefulObject.java
@@ -0,0 +1,529 @@
+//Licensed under Apache License version 2.0
+package javax.jmdns.impl;
+
+import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
+import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
+
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSState;
+import javax.jmdns.impl.tasks.DNSTask;
+
+/**
+ * Sets of methods to manage the state machine.<br/>
+ * <b>Implementation note:</b> This interface is accessed from multiple threads. The implementation must be thread safe.
+ *
+ * @version %I%, %G%
+ * @author Pierre Frisch
+ */
+public interface DNSStatefulObject
+{
+
+ public static class DefaultImplementation extends ReentrantLock implements DNSStatefulObject
+ {
+ private static Logger logger = Logger.getLogger(DefaultImplementation.class.getName());
+
+ private static final long serialVersionUID = -3264781576883412227L;
+
+ private volatile JmDNSImpl _dns;
+
+ protected volatile DNSTask _task;
+
+ protected volatile DNSState _state;
+
+ public DefaultImplementation()
+ {
+ super();
+ _dns = null;
+ _task = null;
+ _state = DNSState.PROBING_1;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#getDns()
+ */
+ @Override
+ public JmDNSImpl getDns()
+ {
+ return this._dns;
+ }
+
+ protected void setDns(JmDNSImpl dns)
+ {
+ this._dns = dns;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#associateWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public void associateWithTask(DNSTask task, DNSState state)
+ {
+ if (this._task == null && this._state == state)
+ {
+ this.lock();
+ try
+ {
+ if (this._task == null && this._state == state)
+ {
+ this.setTask(task);
+ }
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#removeAssociationWithTask(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public void removeAssociationWithTask(DNSTask task)
+ {
+ if (this._task == task)
+ {
+ this.lock();
+ try
+ {
+ if (this._task == task)
+ {
+ this.setTask(null);
+ }
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#isHandledByTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public boolean isAssociatedWithTask(DNSTask task, DNSState state)
+ {
+ this.lock();
+ try
+ {
+ return this._task == task && this._state == state;
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+
+ protected void setTask(DNSTask task)
+ {
+ this._task = task;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#advanceState(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public boolean advanceState(DNSTask task)
+ {
+ boolean result = true;
+ if (this._task == task)
+ {
+ this.lock();
+ try
+ {
+ if (this._task == task)
+ {
+ this._state = this._state.advance();
+ }
+ else
+ {
+ logger.warning("Trying to advance state whhen not the owner. owner: " + this._task + " perpetrator: " + task);
+ }
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#revertState()
+ */
+ @Override
+ public boolean revertState()
+ {
+ boolean result = true;
+ if (!this.willCancel())
+ {
+ this.lock();
+ try
+ {
+ if (!this.willCancel())
+ {
+ this._state = this._state.revert();
+ this.setTask(null);
+ }
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#cancel()
+ */
+ @Override
+ public boolean cancelState()
+ {
+ boolean result = false;
+ if (!this.willCancel())
+ {
+ this.lock();
+ try
+ {
+ if (!this.willCancel())
+ {
+ this._state = DNSState.CANCELING_1;
+ this.setTask(null);
+ result = true;
+ }
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#recover()
+ */
+ @Override
+ public boolean recoverState()
+ {
+ boolean result = false;
+ this.lock();
+ try
+ {
+ this._state = DNSState.PROBING_1;
+ this.setTask(null);
+ }
+ finally
+ {
+ this.unlock();
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#isProbing()
+ */
+ @Override
+ public boolean isProbing()
+ {
+ return this._state.isProbing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#isAnnouncing()
+ */
+ @Override
+ public boolean isAnnouncing()
+ {
+ return this._state.isAnnouncing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#isAnnounced()
+ */
+ @Override
+ public boolean isAnnounced()
+ {
+ return this._state.isAnnounced();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceling()
+ */
+ @Override
+ public boolean isCanceling()
+ {
+ return this._state.isCanceling();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefullObject#isCanceled()
+ */
+ @Override
+ public boolean isCanceled()
+ {
+ return this._state.isCanceled();
+ }
+
+ private boolean willCancel()
+ {
+ return this._state.isCanceled() || this._state.isCanceling();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForAnnounced(long)
+ */
+ @Override
+ public boolean waitForAnnounced(long timeout)
+ {
+ if (!this.isAnnounced() && !this.willCancel())
+ {
+ try
+ {
+ boolean finished = false;
+ long end = (timeout > 0 ? System.currentTimeMillis() + timeout : Long.MAX_VALUE);
+ while (!finished)
+ {
+ boolean lock = this.tryLock(DNSConstants.ANNOUNCE_WAIT_INTERVAL, TimeUnit.MILLISECONDS);
+ try
+ {
+ finished = (this.isAnnounced() || this.willCancel() ? true : end <= System.currentTimeMillis());
+ }
+ finally
+ {
+ if (lock)
+ {
+ this.unlock();
+ }
+ }
+ }
+ }
+ catch (final InterruptedException e)
+ {
+ // empty
+ }
+ }
+ if (!this.isAnnounced())
+ {
+ if (this.willCancel())
+ {
+ logger.warning("Wait for announced cancelled: " + this);
+ }
+ else
+ {
+ logger.warning("Wait for announced timed out: " + this);
+ }
+ }
+ return this.isAnnounced();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForCanceled(long)
+ */
+ @Override
+ public boolean waitForCanceled(long timeout)
+ {
+ if (!this.isCanceled())
+ {
+ try
+ {
+ boolean finished = false;
+ long end = (timeout > 0 ? System.currentTimeMillis() + timeout : Long.MAX_VALUE);
+ while (!finished)
+ {
+ boolean lock = this.tryLock(DNSConstants.ANNOUNCE_WAIT_INTERVAL, TimeUnit.MILLISECONDS);
+ try
+ {
+ finished = (this.isCanceled() ? true : end <= System.currentTimeMillis());
+ }
+ finally
+ {
+ if (lock)
+ {
+ this.unlock();
+ }
+ }
+ }
+ }
+ catch (final InterruptedException e)
+ {
+ // empty
+ }
+ }
+ if (!this.isCanceled())
+ {
+ logger.warning("Wait for canceled timed out: " + this);
+ }
+ return this.isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return (_dns != null ? "DNS: " + _dns.getName() : "NO DNS") + " state: " + _state + " task: " + _task;
+ }
+
+ }
+
+ /**
+ * Returns the DNS associated with this object.
+ *
+ * @return DNS resolver
+ */
+ public JmDNSImpl getDns();
+
+ /**
+ * Sets the task associated with this Object.
+ *
+ * @param task
+ * associated task
+ * @param state
+ * state of the task
+ */
+ public void associateWithTask(DNSTask task, DNSState state);
+
+ /**
+ * Remove the association of the task with this Object.
+ *
+ * @param task
+ * associated task
+ */
+ public void removeAssociationWithTask(DNSTask task);
+
+ /**
+ * Checks if this object is associated with the task and in the same state.
+ *
+ * @param task
+ * associated task
+ * @param state
+ * state of the task
+ * @return <code>true</code> is the task is associated with this object, <code>false</code> otherwise.
+ */
+ public boolean isAssociatedWithTask(DNSTask task, DNSState state);
+
+ /**
+ * Sets the state and notifies all objects that wait on the ServiceInfo.
+ *
+ * @param task
+ * associated task
+ * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise.
+ *
+ * @see DNSState#advance()
+ */
+ public boolean advanceState(DNSTask task);
+
+ /**
+ * Sets the state and notifies all objects that wait on the ServiceInfo.
+ *
+ * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise.
+ * @see DNSState#revert()
+ */
+ public boolean revertState();
+
+ /**
+ * Sets the state and notifies all objects that wait on the ServiceInfo.
+ *
+ * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise.
+ */
+ public boolean cancelState();
+
+ /**
+ * Sets the state and notifies all objects that wait on the ServiceInfo.
+ *
+ * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise.
+ */
+ public boolean recoverState();
+
+ /**
+ * Returns true, if this is a probing state.
+ *
+ * @return <code>true</code> if probing state, <code>false</code> otherwise
+ */
+ public boolean isProbing();
+
+ /**
+ * Returns true, if this is an announcing state.
+ *
+ * @return <code>true</code> if announcing state, <code>false</code> otherwise
+ */
+ public boolean isAnnouncing();
+
+ /**
+ * Returns true, if this is an announced state.
+ *
+ * @return <code>true</code> if announced state, <code>false</code> otherwise
+ */
+ public boolean isAnnounced();
+
+ /**
+ * Returns true, if this is a canceling state.
+ *
+ * @return <code>true</code> if canceling state, <code>false</code> otherwise
+ */
+ public boolean isCanceling();
+
+ /**
+ * Returns true, if this is a canceled state.
+ *
+ * @return <code>true</code> if canceled state, <code>false</code> otherwise
+ */
+ public boolean isCanceled();
+
+ /**
+ * Waits for the object to be announced.
+ *
+ * @param timeout
+ * the maximum time to wait in milliseconds.
+ * @return <code>true</code> if the object is announced, <code>false</code> otherwise
+ */
+ public boolean waitForAnnounced(long timeout);
+
+ /**
+ * Waits for the object to be canceled.
+ *
+ * @param timeout
+ * the maximum time to wait in milliseconds.
+ * @return <code>true</code> if the object is canceled, <code>false</code> otherwise
+ */
+ public boolean waitForCanceled(long timeout);
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/HostInfo.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/HostInfo.java
index 6acb03fd4..79ae78c18 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/HostInfo.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/HostInfo.java
@@ -2,8 +2,6 @@
//Licensed under Apache License version 2.0
//Original license LGPL
-
-
package javax.jmdns.impl;
import java.io.IOException;
@@ -12,78 +10,186 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+import javax.jmdns.impl.constants.DNSState;
+import javax.jmdns.impl.tasks.DNSTask;
/**
* HostInfo information on the local host to be able to cope with change of addresses.
*
* @version %I%, %G%
- * @author Pierre Frisch, Werner Randelshofer
+ * @author Pierre Frisch, Werner Randelshofer
*/
-public class HostInfo
+public class HostInfo implements DNSStatefulObject
{
-// private static Logger logger = Logger.getLogger(HostInfo.class.getName());
- protected String name;
- protected InetAddress address;
- protected NetworkInterface interfaze;
+ private static Logger logger = Logger.getLogger(HostInfo.class.getName());
+
+ protected String _name;
+
+ protected InetAddress _address;
+
+ protected NetworkInterface _interfaze;
+
+ private final HostInfoState _state;
+
+ private final static class HostInfoState extends DNSStatefulObject.DefaultImplementation
+ {
+
+ private static final long serialVersionUID = -8191476803620402088L;
+
+ /**
+ * @param dns
+ */
+ public HostInfoState(JmDNSImpl dns)
+ {
+ super();
+ this.setDns(dns);
+ }
+
+ }
+
+ public static HostInfo newHostInfo(InetAddress address, JmDNSImpl dns)
+ {
+ HostInfo localhost = null;
+ try
+ {
+ InetAddress addr = address;
+ String aName = "";
+ if (addr == null)
+ {
+ String ip = System.getProperty("net.mdns.interface");
+ if (ip != null)
+ {
+ addr = InetAddress.getByName(ip);
+ }
+ else
+ {
+ addr = InetAddress.getLocalHost();
+ }
+ aName = addr.getHostName();
+ if (addr.isLoopbackAddress())
+ {
+ logger.warning("Could not find any address beside the loopback.");
+ }
+ }
+ else
+ {
+ aName = addr.getHostName();
+ }
+ // A host name with "." is illegal. so strip off everything and append .local.
+ final int idx = aName.indexOf(".");
+ if (idx > 0)
+ {
+ aName = aName.substring(0, idx);
+ }
+ aName += ".local.";
+ localhost = new HostInfo(addr, aName, dns);
+ }
+ catch (final IOException e)
+ {
+ logger.warning("Could not intialize the host network interface on " + address + "because of an error: " + e.getMessage());
+ // This is only used for running unit test on Debian / Ubuntu
+ localhost = new HostInfo(loopbackAddress(), "computer", dns);
+ }
+ return localhost;
+ }
+
+ private static InetAddress loopbackAddress()
+ {
+ try
+ {
+ return InetAddress.getByName(null);
+ }
+ catch (UnknownHostException exception)
+ {
+ return null;
+ }
+ }
+
/**
* This is used to create a unique name for the host name.
*/
private int hostNameCount;
- public HostInfo(InetAddress address, String name)
+ public HostInfo(InetAddress address, String name, JmDNSImpl dns)
{
super();
- this.address = address;
- this.name = name;
+ this._state = new HostInfoState(dns);
+ this._address = address;
+ this._name = name;
if (address != null)
{
try
{
- interfaze = NetworkInterface.getByInetAddress(address);
+ _interfaze = NetworkInterface.getByInetAddress(address);
}
catch (Exception exception)
{
// FIXME Shouldn't we take an action here?
-// logger.log(Level.WARNING, "LocalHostInfo() exception ", exception);
+ logger.log(Level.WARNING, "LocalHostInfo() exception ", exception);
}
}
}
public String getName()
{
- return name;
+ return _name;
+ }
+
+ public InetAddress getInetAddress()
+ {
+ return _address;
}
- public InetAddress getAddress()
+ Inet4Address getInet4Address()
{
- return address;
+ if (this.getInetAddress() instanceof Inet4Address)
+ {
+ return (Inet4Address) _address;
+ }
+ return null;
+ }
+
+ Inet6Address getInet6Address()
+ {
+ if (this.getInetAddress() instanceof Inet6Address)
+ {
+ return (Inet6Address) _address;
+ }
+ return null;
}
public NetworkInterface getInterface()
{
- return interfaze;
+ return _interfaze;
}
synchronized String incrementHostName()
{
hostNameCount++;
- int plocal = name.indexOf(".local.");
- int punder = name.lastIndexOf("-");
- name = name.substring(0, (punder == -1 ? plocal : punder)) + "-" + hostNameCount + ".local.";
- return name;
+ int plocal = _name.indexOf(".local.");
+ int punder = _name.lastIndexOf("-");
+ _name = _name.substring(0, (punder == -1 ? plocal : punder)) + "-" + hostNameCount + ".local.";
+ return _name;
}
boolean shouldIgnorePacket(DatagramPacket packet)
{
boolean result = false;
- if (getAddress() != null)
+ if (this.getInetAddress() != null)
{
InetAddress from = packet.getAddress();
if (from != null)
{
- if (from.isLinkLocalAddress() && (!getAddress().isLinkLocalAddress()))
+ if (from.isLinkLocalAddress() && (!this.getInetAddress().isLinkLocalAddress()))
{
// Ignore linklocal packets on regular interfaces, unless this is
// also a linklocal interface. This is to avoid duplicates. This is
@@ -91,10 +197,9 @@ public class HostInfo
// of the interface on which the packet was received.
result = true;
}
- if (from.isLoopbackAddress() && (!getAddress().isLoopbackAddress()))
+ if (from.isLoopbackAddress() && (!this.getInetAddress().isLoopbackAddress()))
{
- // Ignore loopback packets on a regular interface unless this is
- // also a loopback interface.
+ // Ignore loopback packets on a regular interface unless this is also a loopback interface.
result = true;
}
}
@@ -102,71 +207,237 @@ public class HostInfo
return result;
}
- DNSRecord.Address getDNSAddressRecord(DNSRecord.Address address)
+ DNSRecord.Address getDNSAddressRecord(DNSRecordType type, boolean unique, int ttl)
{
- return (DNSConstants.TYPE_AAAA == address.type ? getDNS6AddressRecord() : getDNS4AddressRecord());
+ switch (type)
+ {
+ case TYPE_A:
+ return this.getDNS4AddressRecord(unique, ttl);
+ case TYPE_AAAA:
+ return this.getDNS6AddressRecord(unique, ttl);
+ default:
+ }
+ return null;
}
- public DNSRecord.Address getDNS4AddressRecord()
+ private DNSRecord.Address getDNS4AddressRecord(boolean unique, int ttl)
{
- if ((getAddress() != null) &&
- ((getAddress() instanceof Inet4Address) ||
- ((getAddress() instanceof Inet6Address) && (((Inet6Address) getAddress()).isIPv4CompatibleAddress()))))
+ if ((this.getInetAddress() instanceof Inet4Address) || ((this.getInetAddress() instanceof Inet6Address) && (((Inet6Address) this.getInetAddress()).isIPv4CompatibleAddress())))
{
- return new DNSRecord.Address(getName(), DNSConstants.TYPE_A, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, getAddress());
+ return new DNSRecord.IPv4Address(this.getName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getInetAddress());
}
return null;
}
- public DNSRecord.Address getDNS6AddressRecord()
+ private DNSRecord.Address getDNS6AddressRecord(boolean unique, int ttl)
{
- if ((getAddress() != null) && (getAddress() instanceof Inet6Address))
+ if (this.getInetAddress() instanceof Inet6Address)
{
- return new DNSRecord.Address(getName(), DNSConstants.TYPE_AAAA, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, getAddress());
+ return new DNSRecord.IPv6Address(this.getName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getInetAddress());
}
return null;
}
+ @Override
public String toString()
{
- StringBuffer buf = new StringBuffer();
+ StringBuilder buf = new StringBuilder(1024);
buf.append("local host info[");
buf.append(getName() != null ? getName() : "no name");
buf.append(", ");
buf.append(getInterface() != null ? getInterface().getDisplayName() : "???");
buf.append(":");
- buf.append(getAddress() != null ? getAddress().getHostAddress() : "no address");
+ buf.append(getInetAddress() != null ? getInetAddress().getHostAddress() : "no address");
+ buf.append(", ");
+ buf.append(_state);
buf.append("]");
return buf.toString();
}
- public void addAddressRecords(DNSOutgoing out, boolean authoritative) throws IOException
+ public Collection<DNSRecord> answers(boolean unique, int ttl)
{
- DNSRecord answer = getDNS4AddressRecord();
+ List<DNSRecord> list = new ArrayList<DNSRecord>();
+ DNSRecord answer = this.getDNS4AddressRecord(unique, ttl);
if (answer != null)
{
- if (authoritative)
- {
- out.addAuthorativeAnswer(answer);
- }
- else
- {
- out.addAnswer(answer, 0);
- }
+ list.add(answer);
}
-
- answer = getDNS6AddressRecord();
+ answer = this.getDNS6AddressRecord(unique, ttl);
if (answer != null)
{
- if (authoritative)
- {
- out.addAuthorativeAnswer(answer);
- }
- else
- {
- out.addAnswer(answer, 0);
- }
+ list.add(answer);
+ }
+ return list;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#getDns()
+ */
+ @Override
+ public JmDNSImpl getDns()
+ {
+ return this._state.getDns();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#advanceState(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public boolean advanceState(DNSTask task)
+ {
+ return this._state.advanceState(task);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#removeAssociationWithTask(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public void removeAssociationWithTask(DNSTask task)
+ {
+ this._state.removeAssociationWithTask(task);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#revertState()
+ */
+ @Override
+ public boolean revertState()
+ {
+ return this._state.revertState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#associateWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public void associateWithTask(DNSTask task, DNSState state)
+ {
+ this._state.associateWithTask(task, state);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAssociatedWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public boolean isAssociatedWithTask(DNSTask task, DNSState state)
+ {
+ return this._state.isAssociatedWithTask(task, state);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#cancel()
+ */
+ @Override
+ public boolean cancelState()
+ {
+ return this._state.cancelState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#recover()
+ */
+ @Override
+ public boolean recoverState()
+ {
+ return this._state.recoverState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isProbing()
+ */
+ @Override
+ public boolean isProbing()
+ {
+ return this._state.isProbing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAnnouncing()
+ */
+ @Override
+ public boolean isAnnouncing()
+ {
+ return this._state.isAnnouncing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAnnounced()
+ */
+ @Override
+ public boolean isAnnounced()
+ {
+ return this._state.isAnnounced();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceling()
+ */
+ @Override
+ public boolean isCanceling()
+ {
+ return this._state.isCanceling();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceled()
+ */
+ @Override
+ public boolean isCanceled()
+ {
+ return this._state.isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForAnnounced(long)
+ */
+ @Override
+ public boolean waitForAnnounced(long timeout)
+ {
+ return _state.waitForAnnounced(timeout);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForCanceled(long)
+ */
+ @Override
+ public boolean waitForCanceled(long timeout)
+ {
+ if (_address == null)
+ {
+ // No need to wait this was never announced.
+ return true;
}
+ return _state.waitForCanceled(timeout);
}
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/JmDNSImpl.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/JmDNSImpl.java
index cbe073a6c..011874a97 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/JmDNSImpl.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/JmDNSImpl.java
@@ -4,1292 +4,2183 @@
package javax.jmdns.impl;
+import java.util.Iterator;
+
import java.io.IOException;
-import java.net.*;
-import java.util.*;
-import javax.jmdns.*;
-import javax.jmdns.impl.tasks.*;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Random;
+import java.util.Set;
+import java.util.Timer;
+import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
+import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
+import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
+import edu.emory.mathcs.backport.java.util.concurrent.Executors;
+import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.JmDNS;
+import javax.jmdns.ServiceEvent;
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.ServiceInfo.Fields;
+import javax.jmdns.ServiceListener;
+import javax.jmdns.ServiceTypeListener;
+import javax.jmdns.impl.ListenerStatus.ServiceListenerStatus;
+import javax.jmdns.impl.ListenerStatus.ServiceTypeListenerStatus;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+import javax.jmdns.impl.constants.DNSState;
+import javax.jmdns.impl.tasks.DNSTask;
+import javax.jmdns.impl.tasks.RecordReaper;
+import javax.jmdns.impl.tasks.Responder;
+import javax.jmdns.impl.tasks.resolver.ServiceInfoResolver;
+import javax.jmdns.impl.tasks.resolver.ServiceResolver;
+import javax.jmdns.impl.tasks.resolver.TypeResolver;
+import javax.jmdns.impl.tasks.state.Announcer;
+import javax.jmdns.impl.tasks.state.Canceler;
+import javax.jmdns.impl.tasks.state.Prober;
+import javax.jmdns.impl.tasks.state.Renewer;
// REMIND: multiple IP addresses
/**
* mDNS implementation in Java.
- *
+ *
* @version %I%, %G%
- * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, Werner Randelshofer,
- * Pierre Frisch, Scott Lewis
+ * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Scott Lewis
*/
-public class JmDNSImpl extends JmDNS {
- // private static Logger logger = Logger.getLogger(JmDNSImpl.class.getName());
-
- // hack for the iPhone (arm darwin) to not set the NetworkInterface. This fails on GNU classpath 0.96.
- private static final boolean isIphone = Boolean.getBoolean("net.mdns.isArmDarwin");
- /**
- * This is the multicast group, we are listening to for multicast DNS
- * messages.
- */
- private InetAddress group;
- /**
- * This is our multicast socket.
- */
- private MulticastSocket socket;
-
- /**
- * Used to fix live lock problem on unregester.
- */
-
- private boolean closed = false;
-
- /**
- * Holds instances of JmDNS.DNSListener. Must by a synchronized collection,
- * because it is updated from concurrent threads.
- */
- private List listeners;
- /**
- * Holds instances of ServiceListener's. Keys are Strings holding a fully
- * qualified service type. Values are LinkedList's of ServiceListener's.
- */
- private Map serviceListeners;
- /**
- * Holds instances of ServiceTypeListener's.
- */
- private List typeListeners;
-
- /**
- * Cache for DNSEntry's.
- */
- private DNSCache cache;
-
- /**
- * This hashtable holds the services that have been registered. Keys are
- * instances of String which hold an all lower-case version of the fully
- * qualified service name. Values are instances of ServiceInfo.
- */
- Map services;
-
- /**
- * This hashtable holds the service types that have been registered or that
- * have been received in an incoming datagram. Keys are instances of String
- * which hold an all lower-case version of the fully qualified service type.
- * Values hold the fully qualified service type.
- */
- Map serviceTypes;
- /**
- * This is the shutdown hook, we registered with the java runtime.
- */
- private Thread shutdown;
-
- /**
- * Handle on the local host
- */
- private HostInfo localHost;
-
- private Thread incomingListener = null;
-
- /**
- * Throttle count. This is used to count the overall number of probes sent
- * by JmDNS. When the last throttle increment happened .
- */
- private int throttle;
- /**
- * Last throttle increment.
- */
- private long lastThrottleIncrement;
-
- /**
- * The timer is used to dispatch all outgoing messages of JmDNS. It is also
- * used to dispatch maintenance tasks for the DNS cache.
- */
- Timer timer;
-
- /**
- * The source for random values. This is used to introduce random delays in
- * responses. This reduces the potential for collisions on the network.
- */
- private final static Random random = new Random();
-
- /**
- * This lock is used to coordinate processing of incoming and outgoing
- * messages. This is needed, because the Rendezvous Conformance Test does
- * not forgive race conditions.
- */
- private Object ioLock = new Object();
-
- /**
- * If an incoming package which needs an answer is truncated, we store it
- * here. We add more incoming DNSRecords to it, until the JmDNS.Responder
- * timer picks it up. Remind: This does not work well with multiple planned
- * answers for packages that came in from different clients.
- */
- private DNSIncoming plannedAnswer;
-
- // State machine
- /**
- * The state of JmDNS. <p/> For proper handling of concurrency, this
- * variable must be changed only using methods advanceState(), revertState()
- * and cancel().
- */
- private DNSState state = DNSState.PROBING_1;
-
- /**
- * Timer task associated to the host name. This is used to prevent from
- * having multiple tasks associated to the host name at the same time.
- */
- private TimerTask task;
-
- /**
- * This hashtable is used to maintain a list of service types being
- * collected by this JmDNS instance. The key of the hashtable is a service
- * type name, the value is an instance of JmDNS.ServiceCollector.
- *
- * @see #list
- */
- private final HashMap serviceCollectors = new HashMap();
-
- /**
- * Create an instance of JmDNS.
- */
- public JmDNSImpl() throws IOException {
- // logger.finer("JmDNS instance created");
- try {
- // determine the interface on which to run on
- InetAddress addr = null;
- String ip = System.getProperty("net.mdns.interface");
- if (ip != null) {
- addr = InetAddress.getByName(ip);
- init(addr, addr.getHostName());
- } else {
- addr = InetAddress.getLocalHost();
- init(addr.isLoopbackAddress() ? null : addr, addr.getHostName());
- }
- // [
- // PJYF
- // Oct
- // 14
- // 2004
- // ]
- // Why
- // do
- // we
- // disallow
- // the
- // loopback
- // address
- // ?
- } catch (final IOException e) {
- init(null, "computer");
- }
- }
-
- /**
- * Create an instance of JmDNS and bind it to a specific network interface
- * given its IP-address.
- */
- public JmDNSImpl(InetAddress addr) throws IOException {
- try {
- init(addr, addr.getHostName());
- } catch (final IOException e) {
- init(null, "computer");
- }
- }
-
- /**
- * Initialize everything.
- *
- * @param address
- * The interface to which JmDNS binds to.
- * @param name
- * The host name of the interface.
- */
- private void init(InetAddress address, String name) throws IOException {
- // A host name with "." is illegal. so strip off everything and append .
- // local.
- final int idx = name.indexOf(".");
- if (idx > 0) {
- name = name.substring(0, idx);
- }
- name += ".local.";
- // localHost to IP address binding
- localHost = new HostInfo(address, name);
-
- cache = new DNSCache(100);
-
- listeners = Collections.synchronizedList(new ArrayList());
- serviceListeners = new HashMap();
- typeListeners = new ArrayList();
-
- services = new Hashtable(20);
- serviceTypes = new Hashtable(20);
-
- // REMIND: If I could pass in a name for the Timer thread,
- // I would pass' JmDNS.Timer'.
- timer = new Timer();
- new RecordReaper(this).start(timer);
- shutdown = new Thread(new Shutdown(), "JmDNS.Shutdown");
- Runtime.getRuntime().addShutdownHook(shutdown);
-
- incomingListener = new Thread(new SocketListener(this), "JmDNS.SocketListener");
-
- // Bind to multicast socket
- openMulticastSocket(getLocalHost());
- start(getServices().values());
- }
-
- private void start(Collection serviceInfos) {
- setState(DNSState.PROBING_1);
- incomingListener.start();
- new Prober(this).start(timer);
- for (final Iterator iterator = serviceInfos.iterator(); iterator.hasNext();) {
- try {
- registerService(new ServiceInfoImpl((ServiceInfoImpl) iterator.next()));
- } catch (final Exception exception) {
- // logger.log(Level.WARNING, "start() Registration exception ", exception);
- }
- }
- }
-
- private void openMulticastSocket(HostInfo hostInfo) throws IOException {
- if (group == null) {
- group = InetAddress.getByName(DNSConstants.MDNS_GROUP);
- }
- if (socket != null) {
- this.closeMulticastSocket();
- }
- socket = new MulticastSocket(DNSConstants.MDNS_PORT);
- if (!isIphone && (hostInfo != null) && (localHost.getInterface() != null)) {
- socket.setNetworkInterface(hostInfo.getInterface());
- }
- socket.setTimeToLive(255);
- socket.joinGroup(group);
- }
-
- private void closeMulticastSocket() {
- // logger.finer("closeMulticastSocket()");
- if (socket != null) {
- // close socket
- try {
- socket.leaveGroup(group);
- socket.close();
- if (incomingListener != null) {
- incomingListener.join();
- }
- } catch (final Exception exception) {
- // logger.log(Level.WARNING, "closeMulticastSocket() Close socket exception ",
- // exception);
- }
- socket = null;
- }
- }
-
- // State machine
- /**
- * Sets the state and notifies all objects that wait on JmDNS.
- */
- public synchronized void advanceState() {
- setState(getState().advance());
- notifyAll();
- }
-
- /**
- * Sets the state and notifies all objects that wait on JmDNS.
- */
- synchronized void revertState() {
- setState(getState().revert());
- notifyAll();
- }
-
- /**
- * Sets the state and notifies all objects that wait on JmDNS.
- */
- synchronized void cancel() {
- setState(DNSState.CANCELED);
- notifyAll();
- }
-
- /**
- * Returns the current state of this info.
- */
- public DNSState getState() {
- return state;
- }
-
- /**
- * Return the DNSCache associated with the cache variable
- */
- public DNSCache getCache() {
- return cache;
- }
-
- /**
- * @see javax.jmdns.JmDNS#getHostName()
- */
- public String getHostName() {
- return localHost.getName();
- }
-
- public HostInfo getLocalHost() {
- return localHost;
- }
-
- /**
- * @see javax.jmdns.JmDNS#getInterface()
- */
- public InetAddress getInterface() throws IOException {
- return socket.getInterface();
- }
-
- /**
- * @see javax.jmdns.JmDNS#getServiceInfo(java.lang.String, java.lang.String)
- */
- public ServiceInfo getServiceInfo(String type, String name) {
- return getServiceInfo(type, name, 3 * 1000);
- }
-
- /**
- * @see javax.jmdns.JmDNS#getServiceInfo(java.lang.String, java.lang.String,
- * int)
- */
- public ServiceInfo getServiceInfo(String type, String name, int timeout) {
- final ServiceInfoImpl info = new ServiceInfoImpl(type, name);
- new ServiceInfoResolver(this, info).start(timer);
-
- try {
- final long end = System.currentTimeMillis() + timeout;
- long delay;
- synchronized (info) {
- while (!info.hasData() && (delay = end - System.currentTimeMillis()) > 0) {
- info.wait(delay);
- }
- }
- } catch (final InterruptedException e) {
- // empty
- }
-
- return (info.hasData()) ? info : null;
- }
-
- /**
- * @see javax.jmdns.JmDNS#requestServiceInfo(java.lang.String,
- * java.lang.String)
- */
- public void requestServiceInfo(String type, String name) {
- requestServiceInfo(type, name, 3 * 1000);
- }
-
- /**
- * @see javax.jmdns.JmDNS#requestServiceInfo(java.lang.String,
- * java.lang.String, int)
- */
- public void requestServiceInfo(String type, String name, int timeout) {
- registerServiceType(type);
- final ServiceInfoImpl info = new ServiceInfoImpl(type, name);
- new ServiceInfoResolver(this, info).start(timer);
-
- try {
- final long end = System.currentTimeMillis() + timeout;
- long delay;
- synchronized (info) {
- while (!info.hasData() && (delay = end - System.currentTimeMillis()) > 0) {
- info.wait(delay);
- }
- }
- } catch (final InterruptedException e) {
- // empty
- }
- }
-
- void handleServiceResolved(ServiceInfoImpl info) {
- List list = null;
- ArrayList listCopy = null;
- synchronized (serviceListeners) {
- list = (List) serviceListeners.get(info.type.toLowerCase());
-
- if (list != null) {
- listCopy = new ArrayList(list);
- }
- }
- if (listCopy != null) {
- final ServiceEvent event = new ServiceEventImpl(this, info.type, info.getName(), info);
- for (final Iterator iterator = listCopy.iterator(); iterator.hasNext();) {
- ((ServiceListener) iterator.next()).serviceResolved(event);
- }
- }
- }
-
- /**
- * @see
- * javax.jmdns.JmDNS#addServiceTypeListener(javax.jmdns.ServiceTypeListener
- * )
- */
- public void addServiceTypeListener(ServiceTypeListener listener) throws IOException {
- synchronized (this) {
- typeListeners.remove(listener);
- typeListeners.add(listener);
- }
-
- // report cached service types
- for (final Iterator iterator = serviceTypes.values().iterator(); iterator.hasNext();) {
- listener.serviceTypeAdded(new ServiceEventImpl(this, (String) iterator.next(), null, null));
- }
-
- new TypeResolver(this).start(timer);
- }
-
- /**
- * @see javax.jmdns.JmDNS#removeServiceTypeListener(javax.jmdns.
- * ServiceTypeListener)
- */
- public void removeServiceTypeListener(ServiceTypeListener listener) {
- synchronized (this) {
- typeListeners.remove(listener);
- }
- }
-
- /**
- * @see javax.jmdns.JmDNS#addServiceListener(java.lang.String,
- * javax.jmdns.ServiceListener)
- */
- public void addServiceListener(String type, ServiceListener listener) {
- final String lotype = type.toLowerCase();
- removeServiceListener(lotype, listener);
- List list = null;
-
- synchronized (serviceListeners) {
- list = (List) serviceListeners.get(lotype);
- if (list == null) {
- list = Collections.synchronizedList(new LinkedList());
- serviceListeners.put(lotype, list);
- }
- list.add(listener);
- }
-
- // report cached service types
- final List serviceEvents = new ArrayList();
- synchronized (cache) {
- for (final Iterator i = cache.iterator(); i.hasNext();) {
- for (DNSCache.CacheNode n = (DNSCache.CacheNode) i.next(); n != null; n = n.next()) {
- final DNSRecord rec = (DNSRecord) n.getValue();
- if (rec.type == DNSConstants.TYPE_SRV) {
- if (rec.name.endsWith(type)) {
- serviceEvents.add(new ServiceEventImpl(this, type, toUnqualifiedName(type, rec.name), null));
- }
- }
- }
- }
- }
- // Actually call listener with all service events added above
- for (final Iterator i = serviceEvents.iterator(); i.hasNext();) {
- listener.serviceAdded((ServiceEventImpl) i.next());
- }
- // Create/start ServiceResolver
- new ServiceResolver(this, type).start(timer);
- }
-
- /**
- * @see javax.jmdns.JmDNS#removeServiceListener(java.lang.String,
- * javax.jmdns.ServiceListener)
- */
- public void removeServiceListener(String type, ServiceListener listener) {
- type = type.toLowerCase();
- List list = null;
- synchronized (serviceListeners) {
- list = (List) serviceListeners.get(type);
- if (list != null) {
- list.remove(listener);
- if (list.size() == 0) {
- serviceListeners.remove(type);
- }
- }
- }
- }
-
- /**
- * @see javax.jmdns.JmDNS#registerService(javax.jmdns.ServiceInfo)
- */
- public void registerService(ServiceInfo infoAbstract) throws IOException {
- final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
-
- registerServiceType(info.type);
-
- // bind the service to this address
- info.server = localHost.getName();
- info.addr = localHost.getAddress();
-
- synchronized (this) {
- makeServiceNameUnique(info);
- services.put(info.getQualifiedName().toLowerCase(), info);
- }
-
- new /* Service */Prober(this).start(timer);
- try {
- synchronized (info) {
- while (info.getState().compareTo(DNSState.ANNOUNCED) < 0) {
- info.wait();
- }
- }
- } catch (final InterruptedException e) {
- // empty
- }
- // logger.fine("registerService() JmDNS registered service as " + info);
- }
-
- /**
- * @see javax.jmdns.JmDNS#unregisterService(javax.jmdns.ServiceInfo)
- */
- public void unregisterService(ServiceInfo infoAbstract) {
- final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
- synchronized (this) {
- services.remove(info.getQualifiedName().toLowerCase());
- }
- info.cancel();
-
- // Note: We use this lock object to synchronize on it.
- // Synchronizing on another object (e.g. the ServiceInfo) does
- // not make sense, because the sole purpose of the lock is to
- // wait until the canceler has finished. If we synchronized on
- // the ServiceInfo or on the Canceler, we would block all
- // accesses to synchronized methods on that object. This is not
- // what we want!
- final Object lock = new Object();
-
- // Remind: We get a deadlock here, if the Canceler does not run!
- try {
- synchronized (lock) {
- new Canceler(this, info, lock).start(timer);
- lock.wait();
- }
- } catch (final InterruptedException e) {
- // empty
- }
- }
-
- /**
- * @see javax.jmdns.JmDNS#unregisterAllServices()
- */
- public void unregisterAllServices() {
- // logger.finer("unregisterAllServices()");
- if (services.size() == 0) {
- return;
- }
-
- Collection list;
- synchronized (this) {
- list = new LinkedList(services.values());
- services.clear();
- }
- for (final Iterator iterator = list.iterator(); iterator.hasNext();) {
- ((ServiceInfoImpl) iterator.next()).cancel();
- }
-
- final Object lock = new Object();
- new Canceler(this, list, lock).start(timer);
- // Remind: We get a livelock here, if the Canceler does not run!
- try {
- synchronized (lock) {
- if (!closed) {
- lock.wait();
- }
- }
- } catch (final InterruptedException e) {
- // empty
- }
-
- }
-
- /**
- * @see javax.jmdns.JmDNS#registerServiceType(java.lang.String)
- */
- public void registerServiceType(String type) {
- final String name = type.toLowerCase();
- if (serviceTypes.get(name) == null) {
- if ((type.indexOf(DNSConstants.DNS_META_QUERY) < 0) && !type.endsWith(".in-addr.arpa.")) {
- Collection list;
- synchronized (this) {
- serviceTypes.put(name, type);
- list = new LinkedList(typeListeners);
- }
- for (final Iterator iterator = list.iterator(); iterator.hasNext();) {
- ((ServiceTypeListener) iterator.next()).serviceTypeAdded(new ServiceEventImpl(this, type, null, null));
- }
- }
- }
- }
-
- /**
- * Generate a possibly unique name for a host using the information we have
- * in the cache.
- *
- * @return returns true, if the name of the host had to be changed.
- */
- // private boolean makeHostNameUnique(DNSRecord.Address host)
- // {
- // final String originalName = host.getName();
- // System.currentTimeMillis();
- //
- // boolean collision;
- // do
- // {
- // collision = false;
- //
- // // Check for collision in cache
- // for (DNSCache.CacheNode j = cache.find(host.getName().toLowerCase()); j != null; j = j
- // .next())
- // {
- // if (false)
- // {
- // host.name = incrementName(host.getName());
- // collision = true;
- // break;
- // }
- // }
- // }
- // while (collision);
- //
- // if (originalName.equals(host.getName()))
- // {
- // return false;
- // }
- // else
- // {
- // return true;
- // }
- // }
-
- /**
- * Generate a possibly unique name for a service using the information we
- * have in the cache.
- *
- * @return returns true, if the name of the service info had to be changed.
- */
- private boolean makeServiceNameUnique(ServiceInfoImpl info) {
- final String originalQualifiedName = info.getQualifiedName();
- final long now = System.currentTimeMillis();
-
- boolean collision;
- do {
- collision = false;
-
- // Check for collision in cache
- for (DNSCache.CacheNode j = cache.find(info.getQualifiedName().toLowerCase()); j != null; j = j.next()) {
- final DNSRecord a = (DNSRecord) j.getValue();
- if ((a.type == DNSConstants.TYPE_SRV) && !a.isExpired(now)) {
- final DNSRecord.Service s = (DNSRecord.Service) a;
- if (s.port != info.port || !s.server.equals(localHost.getName())) {
- // logger
- // .finer("makeServiceNameUnique() JmDNS.makeServiceNameUnique srv collision:"
- // + a
- // + " s.server="
- // + s.server
- // + " "
- // + localHost.getName()
- // + " equals:" + (s.server.equals(localHost.getName())));
- info.setName(incrementName(info.getName()));
- collision = true;
- break;
- }
- }
- }
-
- // Check for collision with other service infos published by JmDNS
- final Object selfService = services.get(info.getQualifiedName().toLowerCase());
- if (selfService != null && selfService != info) {
- info.setName(incrementName(info.getName()));
- collision = true;
- }
- } while (collision);
-
- return !(originalQualifiedName.equals(info.getQualifiedName()));
- }
-
- String incrementName(String name) {
- try {
- final int l = name.lastIndexOf('(');
- final int r = name.lastIndexOf(')');
- if ((l >= 0) && (l < r)) {
- name = name.substring(0, l) + "(" + (Integer.parseInt(name.substring(l + 1, r)) + 1) + ")";
- } else {
- name += " (2)";
- }
- } catch (final NumberFormatException e) {
- name += " (2)";
- }
- return name;
- }
-
- /**
- * Add a listener for a question. The listener will receive updates of
- * answers to the question as they arrive, or from the cache if they are
- * already available.
- */
- public void addListener(DNSListener listener, DNSQuestion question) {
- final long now = System.currentTimeMillis();
-
- // add the new listener
- synchronized (this) {
- listeners.add(listener);
- }
-
- // report existing matched records
- if (question != null) {
- for (DNSCache.CacheNode i = cache.find(question.name); i != null; i = i.next()) {
- final DNSRecord c = (DNSRecord) i.getValue();
- if (question.answeredBy(c) && !c.isExpired(now)) {
- listener.updateRecord(this, now, c);
- }
- }
- }
- }
-
- /**
- * Remove a listener from all outstanding questions. The listener will no
- * longer receive any updates.
- */
- public void removeListener(DNSListener listener) {
- synchronized (this) {
- listeners.remove(listener);
- }
- }
-
- // Remind: Method updateRecord should receive a better name.
- /**
- * Notify all listeners that a record was updated.
- */
- public void updateRecord(long now, DNSRecord rec) {
- // We do not want to block the entire DNS while we are updating the
- // record for each listener (service info)
- List listenerList = null;
- synchronized (this) {
- listenerList = new ArrayList(listeners);
- }
- for (final Iterator iterator = listenerList.iterator(); iterator.hasNext();) {
- final DNSListener listener = (DNSListener) iterator.next();
- listener.updateRecord(this, now, rec);
- }
- if (rec.type == DNSConstants.TYPE_PTR || rec.type == DNSConstants.TYPE_SRV) {
- List serviceListenerList = null;
- synchronized (serviceListeners) {
- serviceListenerList = (List) serviceListeners.get(rec.name.toLowerCase());
- // Iterate on a copy in case listeners will modify it
- if (serviceListenerList != null) {
- serviceListenerList = new ArrayList(serviceListenerList);
- }
- }
- if (serviceListenerList != null) {
- final boolean expired = rec.isExpired(now);
- final String type = rec.getName();
- final String name = ((DNSRecord.Pointer) rec).getAlias();
- // DNSRecord old = (DNSRecord)services.get(name.toLowerCase());
- if (!expired) {
- // new record
- final ServiceEvent event = new ServiceEventImpl(this, type, toUnqualifiedName(type, name), null);
- for (final Iterator iterator = serviceListenerList.iterator(); iterator.hasNext();) {
- ((ServiceListener) iterator.next()).serviceAdded(event);
- }
- } else {
- // expire record
- final ServiceEvent event = new ServiceEventImpl(this, type, toUnqualifiedName(type, name), null);
- for (final Iterator iterator = serviceListenerList.iterator(); iterator.hasNext();) {
- ((ServiceListener) iterator.next()).serviceRemoved(event);
- }
- }
- }
- }
- }
-
- /**
- * Handle an incoming response. Cache answers, and pass them on to the
- * appropriate questions.
- */
- void handleResponse(DNSIncoming msg) throws IOException {
- final long now = System.currentTimeMillis();
-
- boolean hostConflictDetected = false;
- boolean serviceConflictDetected = false;
-
- for (final Iterator i = msg.answers.iterator(); i.hasNext();) {
- boolean isInformative = false;
- DNSRecord rec = (DNSRecord) i.next();
- final boolean expired = rec.isExpired(now);
-
- // update the cache
- final DNSRecord c = (DNSRecord) cache.get(rec);
- if (c != null) {
- if (expired) {
- isInformative = true;
- cache.remove(c);
- } else {
- c.resetTTL(rec);
- rec = c;
- }
- } else {
- if (!expired) {
- isInformative = true;
- cache.add(rec);
- }
- }
- switch (rec.type) {
- case DNSConstants.TYPE_PTR :
- // handle _mdns._udp records
- if (rec.getName().indexOf(DNSConstants.DNS_META_QUERY) >= 0) {
- if (!expired && rec.name.startsWith("_services" + DNSConstants.DNS_META_QUERY)) {
- isInformative = true;
- registerServiceType(((DNSRecord.Pointer) rec).alias);
- }
- continue;
- }
- registerServiceType(rec.name);
- break;
- }
-
- if ((rec.getType() == DNSConstants.TYPE_A) || (rec.getType() == DNSConstants.TYPE_AAAA)) {
- hostConflictDetected |= rec.handleResponse(this);
- } else {
- serviceConflictDetected |= rec.handleResponse(this);
- }
-
- // notify the listeners
- if (isInformative) {
- updateRecord(now, rec);
- }
- }
-
- if (hostConflictDetected || serviceConflictDetected) {
- new Prober(this).start(timer);
- }
- }
-
- /**
- * Handle an incoming query. See if we can answer any part of it given our
- * service infos.
- */
- void handleQuery(DNSIncoming in, InetAddress addr, int port) throws IOException {
- // Track known answers
- boolean hostConflictDetected = false;
- boolean serviceConflictDetected = false;
- final long expirationTime = System.currentTimeMillis() + DNSConstants.KNOWN_ANSWER_TTL;
- for (final Iterator i = in.answers.iterator(); i.hasNext();) {
- final DNSRecord answer = (DNSRecord) i.next();
- if ((answer.getType() == DNSConstants.TYPE_A) || (answer.getType() == DNSConstants.TYPE_AAAA)) {
- hostConflictDetected |= answer.handleQuery(this, expirationTime);
- } else {
- serviceConflictDetected |= answer.handleQuery(this, expirationTime);
- }
- }
-
- if (plannedAnswer != null) {
- plannedAnswer.append(in);
- } else {
- if (in.isTruncated()) {
- plannedAnswer = in;
- }
-
- new Responder(this, in, addr, port).start();
- }
-
- if (hostConflictDetected || serviceConflictDetected) {
- new Prober(this).start(timer);
- }
- }
-
- /**
- * Add an answer to a question. Deal with the case when the outgoing packet
- * overflows
- */
- public DNSOutgoing addAnswer(DNSIncoming in, InetAddress addr, int port, DNSOutgoing out, DNSRecord rec) throws IOException {
- if (out == null) {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- }
- try {
- out.addAnswer(in, rec);
- } catch (final IOException e) {
- out.flags |= DNSConstants.FLAGS_TC;
- out.id = in.id;
- out.finish();
- send(out);
-
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- out.addAnswer(in, rec);
- }
- return out;
- }
-
- /**
- * Send an outgoing multicast DNS message.
- */
- public void send(DNSOutgoing out) throws IOException {
- out.finish();
- if (!out.isEmpty()) {
- final DatagramPacket packet = new DatagramPacket(out.data, out.off, group, DNSConstants.MDNS_PORT);
-
- try {
- final DNSIncoming msg = new DNSIncoming(packet);
- // logger.finest("send() JmDNS out:" + msg.print(true));
- } catch (final IOException e) {
- // logger.throwing(getClass().toString(),
- // "send(DNSOutgoing) - JmDNS can not parse what it sends!!!", e);
- }
- final MulticastSocket ms = socket;
- if (ms != null && !ms.isClosed())
- ms.send(packet);
- }
- }
-
- public void startAnnouncer() {
- if (getState() != DNSState.CANCELED)
- new Announcer(this).start(timer);
- }
-
- public void startRenewer() {
- if (getState() != DNSState.CANCELED)
- new Renewer(this).start(timer);
- }
-
- public void schedule(TimerTask task, int delay) {
- if (getState() != DNSState.CANCELED)
- timer.schedule(task, delay);
- }
-
- // REMIND: Why is this not an anonymous inner class?
- /**
- * Shutdown operations.
- */
- private class Shutdown implements Runnable {
- public void run() {
- shutdown = null;
- close();
- }
- }
-
- /**
- * Recover jmdns when there is an error.
- */
- public void recover() {
- // logger.finer("recover()");
- // We have an IO error so lets try to recover if anything happens lets
- // close it.
- // This should cover the case of the IP address changing under our feet
- if (DNSState.CANCELED != getState()) {
- synchronized (this) { // Synchronize only if we are not already in process to prevent
- // dead locks
- //
- // logger.finer("recover() Cleanning up");
- // Stop JmDNS
- setState(DNSState.CANCELED); // This protects against recursive
- // calls
-
- // We need to keep a copy for reregistration
- final Collection oldServiceInfos = new ArrayList(getServices().values());
-
- // Cancel all services
- unregisterAllServices();
- disposeServiceCollectors();
- //
- // close multicast socket
- closeMulticastSocket();
- //
- cache.clear();
- // logger.finer("recover() All is clean");
- //
- // All is clear now start the services
- //
- try {
- openMulticastSocket(getLocalHost());
- start(oldServiceInfos);
- } catch (final Exception exception) {
- // logger.log(Level.WARNING, "recover() Start services exception ", exception);
- }
- // logger.log(Level.WARNING, "recover() We are back!");
- }
- }
- }
-
- /**
- * @see javax.jmdns.JmDNS#close()
- */
- public void close() {
- if (getState() != DNSState.CANCELED) {
- synchronized (this) { // Synchronize only if we are not already in process to prevent
- // dead locks
- // Stop JmDNS
- setState(DNSState.CANCELED); // This protects against recursive
- // calls
-
- unregisterAllServices();
-
- // Stop the timer
- timer.cancel();
-
- disposeServiceCollectors();
-
- // close socket
- closeMulticastSocket();
-
- // remove the shutdown hook
- if (shutdown != null) {
- Runtime.getRuntime().removeShutdownHook(shutdown);
- }
-
- }
- }
- }
-
- /**
- * List cache entries, for debugging only.
- */
- void print() {
- System.out.println("---- cache ----");
- cache.print();
- System.out.println();
- }
-
- /**
- * @see javax.jmdns.JmDNS#printServices()
- */
- public void printServices() {
- System.err.println(toString());
- }
-
- public String toString() {
- final StringBuffer aLog = new StringBuffer();
- aLog.append("\t---- Services -----");
- if (services != null) {
- for (final Iterator k = services.keySet().iterator(); k.hasNext();) {
- final Object key = k.next();
- aLog.append("\n\t\tService: " + key + ": " + services.get(key));
- }
- }
- aLog.append("\n");
- aLog.append("\t---- Types ----");
- if (serviceTypes != null) {
- for (final Iterator k = serviceTypes.keySet().iterator(); k.hasNext();) {
- final Object key = k.next();
- aLog.append("\n\t\tType: " + key + ": " + serviceTypes.get(key));
- }
- }
- aLog.append("\n");
- aLog.append(cache.toString());
- aLog.append("\n");
- aLog.append("\t---- Service Collectors ----");
- if (serviceCollectors != null) {
- synchronized (serviceCollectors) {
- for (final Iterator k = serviceCollectors.keySet().iterator(); k.hasNext();) {
- final Object key = k.next();
- aLog.append("\n\t\tService Collector: " + key + ": " + serviceCollectors.get(key));
- }
- serviceCollectors.clear();
- }
- }
- return aLog.toString();
- }
-
- /**
- * @see javax.jmdns.JmDNS#list(java.lang.String)
- */
- public ServiceInfo[] list(String type) {
- // Implementation note: The first time a list for a given type is
- // requested, a ServiceCollector is created which collects service
- // infos. This greatly speeds up the performance of subsequent calls
- // to this method. The caveats are, that 1) the first call to this
- // method
- // for a given type is slow, and 2) we spawn a ServiceCollector
- // instance for each service type which increases network traffic a
- // little.
-
- ServiceCollector collector;
-
- boolean newCollectorCreated;
- synchronized (serviceCollectors) {
- collector = (ServiceCollector) serviceCollectors.get(type);
- if (collector == null) {
- collector = new ServiceCollector(type);
- serviceCollectors.put(type, collector);
- addServiceListener(type, collector);
- newCollectorCreated = true;
- } else {
- newCollectorCreated = false;
- }
- }
-
- // After creating a new ServiceCollector, we collect service infos for
- // 200 milliseconds. This should be enough time, to get some service
- // infos from the network.
- if (newCollectorCreated) {
- try {
- Thread.sleep(200);
- } catch (final InterruptedException e) {
- }
- }
-
- return collector.list();
- }
-
- /**
- * This method disposes all ServiceCollector instances which have been
- * created by calls to method <code>list(type)</code>.
- *
- * @see #list
- */
- private void disposeServiceCollectors() {
- // logger.finer("disposeServiceCollectors()");
- synchronized (serviceCollectors) {
- for (final Iterator i = serviceCollectors.values().iterator(); i.hasNext();) {
- final ServiceCollector collector = (ServiceCollector) i.next();
- removeServiceListener(collector.type, collector);
- }
- serviceCollectors.clear();
- }
- }
-
- /**
- * Instances of ServiceCollector are used internally to speed up the
- * performance of method <code>list(type)</code>.
- *
- * @see #list
- */
- private static class ServiceCollector implements ServiceListener {
- // private static Logger logger = Logger.getLogger(ServiceCollector.class.getName());
- /**
- * A set of collected service instance names.
- */
- private final Map infos = Collections.synchronizedMap(new HashMap());
-
- public String type;
-
- public ServiceCollector(String type) {
- this.type = type;
- }
-
- /**
- * A service has been added.
- */
- public void serviceAdded(ServiceEvent event) {
- synchronized (infos) {
- event.getDNS().requestServiceInfo(event.getType(), event.getName(), 0);
- }
- }
-
- /**
- * A service has been removed.
- */
- public void serviceRemoved(ServiceEvent event) {
- synchronized (infos) {
- infos.remove(event.getName());
- }
- }
-
- /**
- * A service hase been resolved. Its details are now available in the
- * ServiceInfo record.
- */
- public void serviceResolved(ServiceEvent event) {
- synchronized (infos) {
- infos.put(event.getName(), event.getInfo());
- }
- }
-
- /**
- * Returns an array of all service infos which have been collected by
- * this ServiceCollector.
- */
- public ServiceInfoImpl[] list() {
- synchronized (infos) {
- return (ServiceInfoImpl[]) infos.values().toArray(new ServiceInfoImpl[infos.size()]);
- }
- }
-
- public String toString() {
- final StringBuffer aLog = new StringBuffer();
- synchronized (infos) {
- for (final Iterator k = infos.keySet().iterator(); k.hasNext();) {
- final Object key = k.next();
- aLog.append("\n\t\tService: " + key + ": " + infos.get(key));
- }
- }
- return aLog.toString();
- }
- };
-
- private static String toUnqualifiedName(String type, String qualifiedName) {
- if (qualifiedName.endsWith(type)) {
- return qualifiedName.substring(0, qualifiedName.length() - type.length() - 1);
- } else {
- return qualifiedName;
- }
- }
-
- public void setState(DNSState state) {
- this.state = state;
- }
-
- public void setTask(TimerTask task) {
- this.task = task;
- }
-
- public TimerTask getTask() {
- return task;
- }
-
- public Map getServices() {
- return services;
- }
-
- public void setLastThrottleIncrement(long lastThrottleIncrement) {
- this.lastThrottleIncrement = lastThrottleIncrement;
- }
-
- public long getLastThrottleIncrement() {
- return lastThrottleIncrement;
- }
-
- public void setThrottle(int throttle) {
- this.throttle = throttle;
- }
-
- public int getThrottle() {
- return throttle;
- }
-
- public static Random getRandom() {
- return random;
- }
-
- public void setIoLock(Object ioLock) {
- this.ioLock = ioLock;
- }
-
- public Object getIoLock() {
- return ioLock;
- }
-
- public void setPlannedAnswer(DNSIncoming plannedAnswer) {
- this.plannedAnswer = plannedAnswer;
- }
-
- public DNSIncoming getPlannedAnswer() {
- return plannedAnswer;
- }
-
- void setLocalHost(HostInfo localHost) {
- this.localHost = localHost;
- }
-
- public Map getServiceTypes() {
- return serviceTypes;
- }
-
- public void setClosed(boolean closed) {
- this.closed = closed;
- }
-
- public boolean isClosed() {
- return closed;
- }
-
- public MulticastSocket getSocket() {
- return socket;
- }
-
- public InetAddress getGroup() {
- return group;
- }
+@SuppressWarnings("unchecked")
+public class JmDNSImpl extends JmDNS implements DNSStatefulObject
+{
+ private static Logger logger = Logger.getLogger(JmDNSImpl.class.getName());
+
+ public enum Operation
+ {
+ Remove, Update, Add, RegisterServiceType, Noop
+ }
+
+ /**
+ * This is the multicast group, we are listening to for multicast DNS messages.
+ */
+ private volatile InetAddress _group;
+ /**
+ * This is our multicast socket.
+ */
+ private volatile MulticastSocket _socket;
+
+ /**
+ * Used to fix live lock problem on unregister.
+ */
+ private volatile boolean _closed = false;
+
+ /**
+ * Holds instances of JmDNS.DNSListener. Must by a synchronized collection, because it is updated from concurrent threads.
+ */
+ private final List<DNSListener> _listeners;
+
+ /**
+ * Holds instances of ServiceListener's. Keys are Strings holding a fully qualified service type. Values are LinkedList's of ServiceListener's.
+ */
+ private final ConcurrentMap/*<String, List<ServiceListenerStatus>>*/ _serviceListeners;
+
+ /**
+ * Holds instances of ServiceTypeListener's.
+ */
+ private final Set<ServiceTypeListenerStatus> _typeListeners;
+
+ /**
+ * Cache for DNSEntry's.
+ */
+ private DNSCache _cache;
+
+ /**
+ * This hashtable holds the services that have been registered. Keys are instances of String which hold an all lower-case version of the fully qualified service name. Values are instances of ServiceInfo.
+ */
+ private final ConcurrentMap/*<String, ServiceInfo>*/ _services;
+
+ /**
+ * This hashtable holds the service types that have been registered or that have been received in an incoming datagram. Keys are instances of String which hold an all lower-case version of the fully qualified service type. Values hold the fully
+ * qualified service type.
+ */
+ private final ConcurrentMap/*<String, Set<String>>*/ _serviceTypes;
+
+ /**
+ * This is the shutdown hook, we registered with the java runtime.
+ */
+ protected Thread _shutdown;
+
+ /**
+ * Handle on the local host
+ */
+ private HostInfo _localHost;
+
+ private Thread _incomingListener;
+
+ /**
+ * Throttle count. This is used to count the overall number of probes sent by JmDNS. When the last throttle increment happened .
+ */
+ private int _throttle;
+
+ /**
+ * Last throttle increment.
+ */
+ private long _lastThrottleIncrement;
+
+ private final ExecutorService _executor = Executors.newSingleThreadExecutor();
+
+ //
+ // 2009-09-16 ldeck: adding docbug patch with slight ammendments
+ // 'Fixes two deadlock conditions involving JmDNS.close() - ID: 1473279'
+ //
+ // ---------------------------------------------------
+ /**
+ * The timer that triggers our announcements. We can't use the main timer object, because that could cause a deadlock where Prober waits on JmDNS.this lock held by close(), close() waits for us to finish, and we wait for Prober to give us back
+ * the timer thread so we can announce. (Patch from docbug in 2006-04-19 still wasn't patched .. so I'm doing it!)
+ */
+ // private final Timer _cancelerTimer;
+ // ---------------------------------------------------
+
+ /**
+ * The timer is used to dispatch all outgoing messages of JmDNS. It is also used to dispatch maintenance tasks for the DNS cache.
+ */
+ private final Timer _timer;
+
+ /**
+ * The timer is used to dispatch maintenance tasks for the DNS cache.
+ */
+ private final Timer _stateTimer;
+
+ /**
+ * The source for random values. This is used to introduce random delays in responses. This reduces the potential for collisions on the network.
+ */
+ private final static Random _random = new Random();
+
+ /**
+ * This lock is used to coordinate processing of incoming and outgoing messages. This is needed, because the Rendezvous Conformance Test does not forgive race conditions.
+ */
+ private final ReentrantLock _ioLock = new ReentrantLock();
+
+ /**
+ * If an incoming package which needs an answer is truncated, we store it here. We add more incoming DNSRecords to it, until the JmDNS.Responder timer picks it up.<br/>
+ * FIXME [PJYF June 8 2010]: This does not work well with multiple planned answers for packages that came in from different clients.
+ */
+ private DNSIncoming _plannedAnswer;
+
+ // State machine
+
+ /**
+ * This hashtable is used to maintain a list of service types being collected by this JmDNS instance. The key of the hashtable is a service type name, the value is an instance of JmDNS.ServiceCollector.
+ *
+ * @see #list
+ */
+ private final ConcurrentMap/*<String, ServiceCollector>*/ _serviceCollectors;
+
+ private final String _name;
+
+ /**
+ * Main method to display API information if run from java -jar
+ * <p>
+ *
+ * @param argv
+ * the command line arguments
+ */
+ public static void main(String[] argv)
+ {
+ String version = null;
+ try
+ {
+ final Properties pomProperties = new Properties();
+ pomProperties.load(JmDNSImpl.class.getResourceAsStream("/META-INF/maven/javax.jmdns/jmdns/pom.properties"));
+ version = pomProperties.getProperty("version");
+ }
+ catch (Exception e)
+ {
+ version = "RUNNING.IN.IDE.FULL";
+ }
+ System.out.println("JmDNS version \"" + version + "\"");
+ System.out.println(" ");
+
+ System.out.println("Running on java version \"" + System.getProperty("java.version") + "\"" + " (build " + System.getProperty("java.runtime.version") + ")" + " from " + System.getProperty("java.vendor"));
+
+ System.out.println("Operating environment \"" + System.getProperty("os.name") + "\"" + " version " + System.getProperty("os.version") + " on " + System.getProperty("os.arch"));
+
+ System.out.println("For more information on JmDNS please visit https://sourceforge.net/projects/jmdns/");
+ }
+
+ /**
+ * Create an instance of JmDNS and bind it to a specific network interface given its IP-address.
+ *
+ * @param address
+ * IP address to bind to.
+ * @param name
+ * name of the newly created JmDNS
+ * @throws IOException
+ */
+ public JmDNSImpl(InetAddress address, String name) throws IOException
+ {
+ super();
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("JmDNS instance created");
+ }
+ _cache = new DNSCache(100);
+
+ _listeners = Collections.synchronizedList(new ArrayList<DNSListener>());
+ _serviceListeners = new ConcurrentHashMap/*<String, List<ServiceListenerStatus>>*/();
+ _typeListeners = Collections.synchronizedSet(new HashSet<ServiceTypeListenerStatus>());
+ _serviceCollectors = new ConcurrentHashMap/*<String, ServiceCollector>*/();
+
+ _services = new ConcurrentHashMap/*<String, ServiceInfo>*/(20);
+ _serviceTypes = new ConcurrentHashMap/*<String, Set<String>>*/(20);
+
+ _localHost = HostInfo.newHostInfo(address, this);
+ _name = (name != null ? name : _localHost.getName());
+
+ _timer = new Timer("JmDNS(" + _name + ").Timer", true);
+ _stateTimer = new Timer("JmDNS(" + _name + ").State.Timer", false);
+ // _cancelerTimer = new Timer("JmDNS.cancelerTimer");
+
+ // (ldeck 2.1.1) preventing shutdown blocking thread
+ // -------------------------------------------------
+ // _shutdown = new Thread(new Shutdown(), "JmDNS.Shutdown");
+ // Runtime.getRuntime().addShutdownHook(_shutdown);
+
+ // -------------------------------------------------
+
+ // Bind to multicast socket
+ this.openMulticastSocket(this.getLocalHost());
+ this.start(this.getServices().values());
+
+ new RecordReaper(this).start(_timer);
+ }
+
+ private void start(Collection<? extends ServiceInfo> serviceInfos)
+ {
+ if (_incomingListener == null)
+ {
+ _incomingListener = new Thread(new SocketListener(this), "JmDNS(" + _name + ").SocketListener");
+ _incomingListener.setDaemon(true);
+ _incomingListener.start();
+ }
+ this.startProber();
+ for (ServiceInfo info : serviceInfos)
+ {
+ try
+ {
+ this.registerService(new ServiceInfoImpl(info));
+ }
+ catch (final Exception exception)
+ {
+ logger.log(Level.WARNING, "start() Registration exception ", exception);
+ }
+ }
+ }
+
+ private void openMulticastSocket(HostInfo hostInfo) throws IOException
+ {
+ if (_group == null)
+ {
+ _group = InetAddress.getByName(DNSConstants.MDNS_GROUP);
+ }
+ if (_socket != null)
+ {
+ this.closeMulticastSocket();
+ }
+ _socket = new MulticastSocket(DNSConstants.MDNS_PORT);
+ if ((hostInfo != null) && (hostInfo.getInterface() != null))
+ {
+ try
+ {
+ _socket.setNetworkInterface(hostInfo.getInterface());
+ }
+ catch (SocketException e)
+ {
+ if (logger.isLoggable(Level.FINE))
+ {
+ logger.fine("openMulticastSocket() Set network interface exception: " + e.getMessage());
+ }
+ }
+ }
+ _socket.setTimeToLive(255);
+ _socket.joinGroup(_group);
+ }
+
+ private void closeMulticastSocket()
+ {
+ // jP: 20010-01-18. See below. We'll need this monitor...
+ // assert (Thread.holdsLock(this));
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("closeMulticastSocket()");
+ }
+ if (_socket != null)
+ {
+ // close socket
+ try
+ {
+ try
+ {
+ _socket.leaveGroup(_group);
+ }
+ catch (SocketException exception)
+ {
+ //
+ }
+ _socket.close();
+ // jP: 20010-01-18. It isn't safe to join() on the listener
+ // thread - it attempts to lock the IoLock object, and deadlock
+ // ensues. Per issue #2933183, changed this to wait on the JmDNS
+ // monitor, checking on each notify (or timeout) that the
+ // listener thread has stopped.
+ //
+ while (_incomingListener != null && _incomingListener.isAlive())
+ {
+ synchronized (this)
+ {
+ try
+ {
+ // wait time is arbitrary, we're really expecting notification.
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("closeMulticastSocket(): waiting for jmDNS monitor");
+ }
+ this.wait(1000);
+ }
+ catch (InterruptedException ignored)
+ {
+ // Ignored
+ }
+ }
+ }
+ _incomingListener = null;
+ }
+ catch (final Exception exception)
+ {
+ logger.log(Level.WARNING, "closeMulticastSocket() Close socket exception ", exception);
+ }
+ _socket = null;
+ }
+ }
+
+ // State machine
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#advanceState(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public boolean advanceState(DNSTask task)
+ {
+ return this._localHost.advanceState(task);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#revertState()
+ */
+ @Override
+ public boolean revertState()
+ {
+ return this._localHost.revertState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#cancel()
+ */
+ @Override
+ public boolean cancelState()
+ {
+ return this._localHost.cancelState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#recover()
+ */
+ @Override
+ public boolean recoverState()
+ {
+ return this._localHost.recoverState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#getDns()
+ */
+ @Override
+ public JmDNSImpl getDns()
+ {
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#associateWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public void associateWithTask(DNSTask task, DNSState state)
+ {
+ this._localHost.associateWithTask(task, state);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#removeAssociationWithTask(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public void removeAssociationWithTask(DNSTask task)
+ {
+ this._localHost.removeAssociationWithTask(task);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAssociatedWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public boolean isAssociatedWithTask(DNSTask task, DNSState state)
+ {
+ return this._localHost.isAssociatedWithTask(task, state);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isProbing()
+ */
+ @Override
+ public boolean isProbing()
+ {
+ return this._localHost.isProbing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAnnouncing()
+ */
+ @Override
+ public boolean isAnnouncing()
+ {
+ return this._localHost.isAnnouncing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAnnounced()
+ */
+ @Override
+ public boolean isAnnounced()
+ {
+ return this._localHost.isAnnounced();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceling()
+ */
+ @Override
+ public boolean isCanceling()
+ {
+ return this._localHost.isCanceling();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceled()
+ */
+ @Override
+ public boolean isCanceled()
+ {
+ return this._localHost.isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForAnnounced(long)
+ */
+ @Override
+ public boolean waitForAnnounced(long timeout)
+ {
+ return this._localHost.waitForAnnounced(timeout);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForCanceled(long)
+ */
+ @Override
+ public boolean waitForCanceled(long timeout)
+ {
+ return this._localHost.waitForCanceled(timeout);
+ }
+
+ /**
+ * Return the DNSCache associated with the cache variable
+ *
+ * @return DNS cache
+ */
+ public DNSCache getCache()
+ {
+ return _cache;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return _name;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getHostName()
+ */
+ @Override
+ public String getHostName()
+ {
+ return _localHost.getName();
+ }
+
+ /**
+ * Returns the local host info
+ *
+ * @return local host info
+ */
+ public HostInfo getLocalHost()
+ {
+ return _localHost;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getInterface()
+ */
+ @Override
+ public InetAddress getInterface() throws IOException
+ {
+ return _socket.getInterface();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getServiceInfo(java.lang.String, java.lang.String)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(String type, String name)
+ {
+ return this.getServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getServiceInfo(java.lang.String, java.lang.String)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(String type, String name, long timeout)
+ {
+ return this.getServiceInfo(type, name, false, timeout);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getServiceInfo(java.lang.String, java.lang.String)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(String type, String name, boolean persistent)
+ {
+ return this.getServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#getServiceInfo(java.lang.String, java.lang.String, int)
+ */
+ @Override
+ public ServiceInfo getServiceInfo(String type, String name, boolean persistent, long timeout)
+ {
+ final ServiceInfoImpl info = this.resolveServiceInfo(type, name, "", persistent);
+ this.waitForInfoData(info, timeout);
+ return (info.hasData() ? info : null);
+ }
+
+ ServiceInfoImpl resolveServiceInfo(String type, String name, String subtype, boolean persistent)
+ {
+ String lotype = type.toLowerCase();
+ this.registerServiceType(lotype);
+ if (_serviceCollectors.putIfAbsent(lotype, new ServiceCollector(lotype)) == null)
+ {
+ this.addServiceListener(lotype, (ServiceListener) _serviceCollectors.get(lotype));
+ }
+
+ // Check if the answer is in the cache.
+ final ServiceInfoImpl info = this.getServiceInfoFromCache(type, name, subtype, persistent);
+ // We still run the resolver to do the dispatch but if the info is already there it will quit immediately
+ new ServiceInfoResolver(this, info).start(_timer);
+
+ return info;
+ }
+
+ ServiceInfoImpl getServiceInfoFromCache(String type, String name, String subtype, boolean persistent)
+ {
+ // Check if the answer is in the cache.
+ ServiceInfoImpl info = new ServiceInfoImpl(type, name, subtype, 0, 0, 0, persistent, (byte[]) null);
+ DNSEntry pointerEntry = this.getCache().getDNSEntry(new DNSRecord.Pointer(type, DNSRecordClass.CLASS_ANY, false, 0, info.getQualifiedName()));
+ if (pointerEntry instanceof DNSRecord)
+ {
+ ServiceInfoImpl cachedInfo = (ServiceInfoImpl) ((DNSRecord) pointerEntry).getServiceInfo(persistent);
+ if (cachedInfo != null)
+ {
+ // To get a complete info record we need to retrieve the service, address and the text bytes.
+
+ Map<Fields, String> map = cachedInfo.getQualifiedNameMap();
+ byte[] srvBytes = null;
+ String server = "";
+ DNSEntry serviceEntry = this.getCache().getDNSEntry(info.getQualifiedName(), DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_ANY);
+ if (serviceEntry instanceof DNSRecord)
+ {
+ ServiceInfo cachedServiceEntryInfo = ((DNSRecord) serviceEntry).getServiceInfo(persistent);
+ if (cachedServiceEntryInfo != null)
+ {
+ cachedInfo = new ServiceInfoImpl(map, cachedServiceEntryInfo.getPort(), cachedServiceEntryInfo.getWeight(), cachedServiceEntryInfo.getPriority(), persistent, (byte[]) null);
+ srvBytes = cachedServiceEntryInfo.getTextBytes();
+ server = cachedServiceEntryInfo.getServer();
+ }
+ }
+ DNSEntry addressEntry = this.getCache().getDNSEntry(server, DNSRecordType.TYPE_A, DNSRecordClass.CLASS_ANY);
+ if (addressEntry instanceof DNSRecord)
+ {
+ ServiceInfo cachedAddressInfo = ((DNSRecord) addressEntry).getServiceInfo(persistent);
+ if (cachedAddressInfo != null)
+ {
+ cachedInfo.setAddress(cachedAddressInfo.getInet4Address());
+ cachedInfo._setText(cachedAddressInfo.getTextBytes());
+ }
+ }
+ addressEntry = this.getCache().getDNSEntry(server, DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_ANY);
+ if (addressEntry instanceof DNSRecord)
+ {
+ ServiceInfo cachedAddressInfo = ((DNSRecord) addressEntry).getServiceInfo(persistent);
+ if (cachedAddressInfo != null)
+ {
+ cachedInfo.setAddress(cachedAddressInfo.getInet6Address());
+ cachedInfo._setText(cachedAddressInfo.getTextBytes());
+ }
+ }
+ DNSEntry textEntry = this.getCache().getDNSEntry(cachedInfo.getQualifiedName(), DNSRecordType.TYPE_TXT, DNSRecordClass.CLASS_ANY);
+ if (textEntry instanceof DNSRecord)
+ {
+ ServiceInfo cachedTextInfo = ((DNSRecord) textEntry).getServiceInfo(persistent);
+ if (cachedTextInfo != null)
+ {
+ cachedInfo._setText(cachedTextInfo.getTextBytes());
+ }
+ }
+ if (cachedInfo.getTextBytes().length == 0)
+ {
+ cachedInfo._setText(srvBytes);
+ }
+ if (cachedInfo.hasData())
+ {
+ info = cachedInfo;
+ }
+ }
+ }
+ return info;
+ }
+
+ private void waitForInfoData(ServiceInfo info, long timeout)
+ {
+ synchronized (info)
+ {
+ long loops = (timeout / 200L);
+ if (loops < 1)
+ {
+ loops = 1;
+ }
+ for (int i = 0; i < loops; i++)
+ {
+ if (info.hasData())
+ {
+ break;
+ }
+ try
+ {
+ info.wait(200);
+ }
+ catch (final InterruptedException e)
+ {
+ /* Stub */
+ }
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#requestServiceInfo(java.lang.String, java.lang.String)
+ */
+ @Override
+ public void requestServiceInfo(String type, String name)
+ {
+ this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean)
+ */
+ @Override
+ public void requestServiceInfo(String type, String name, boolean persistent)
+ {
+ this.requestServiceInfo(type, name, persistent, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#requestServiceInfo(java.lang.String, java.lang.String, int)
+ */
+ @Override
+ public void requestServiceInfo(String type, String name, long timeout)
+ {
+ this.requestServiceInfo(type, name, false, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#requestServiceInfo(java.lang.String, java.lang.String, boolean, int)
+ */
+ @Override
+ public void requestServiceInfo(String type, String name, boolean persistent, long timeout)
+ {
+ final ServiceInfoImpl info = this.resolveServiceInfo(type, name, "", persistent);
+ this.waitForInfoData(info, timeout);
+ }
+
+ void handleServiceResolved(ServiceEvent event)
+ {
+ List<ServiceListenerStatus> list = (List<ServiceListenerStatus>) _serviceListeners.get(event.getType().toLowerCase());
+ final List<ServiceListenerStatus> listCopy;
+ if ((list != null) && (!list.isEmpty()))
+ {
+ if ((event.getInfo() != null) && event.getInfo().hasData())
+ {
+ final ServiceEvent localEvent = event;
+ synchronized (list)
+ {
+ listCopy = new ArrayList<ServiceListenerStatus>(list);
+ }
+ for (final ServiceListenerStatus listener : listCopy)
+ {
+ _executor.submit(new Runnable() {
+ @Override
+ public void run()
+ {
+ listener.serviceResolved(localEvent);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * @see javax.jmdns.JmDNS#addServiceTypeListener(javax.jmdns.ServiceTypeListener )
+ */
+ @Override
+ public void addServiceTypeListener(ServiceTypeListener listener) throws IOException
+ {
+ ServiceTypeListenerStatus status = new ServiceTypeListenerStatus(listener);
+ _typeListeners.add(status);
+
+ // report cached service types
+ for (Iterator<String> itr = _serviceTypes.keySet().iterator(); itr.hasNext();)
+// for (String type : _serviceTypes.keySet())
+ {
+ status.serviceTypeAdded(new ServiceEventImpl(this, itr.next(), "", null));
+ }
+
+ new TypeResolver(this).start(_timer);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#removeServiceTypeListener(javax.jmdns.ServiceTypeListener)
+ */
+ @Override
+ public void removeServiceTypeListener(ServiceTypeListener listener)
+ {
+ ServiceTypeListenerStatus status = new ServiceTypeListenerStatus(listener);
+ _typeListeners.remove(status);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#addServiceListener(java.lang.String, javax.jmdns.ServiceListener)
+ */
+ @Override
+ public void addServiceListener(String type, ServiceListener listener)
+ {
+ ServiceListenerStatus status = new ServiceListenerStatus(listener);
+ final String lotype = type.toLowerCase();
+ List<ServiceListenerStatus> list = (List<ServiceListenerStatus>) _serviceListeners.get(lotype);
+ if (list == null)
+ {
+ if (_serviceListeners.putIfAbsent(lotype, new LinkedList<ServiceListenerStatus>()) == null)
+ {
+ if (_serviceCollectors.putIfAbsent(lotype, new ServiceCollector(lotype)) == null)
+ {
+ this.addServiceListener(lotype, (ServiceListener) _serviceCollectors.get(lotype));
+ }
+ }
+ list = (List<ServiceListenerStatus>) _serviceListeners.get(lotype);
+ }
+ if (list != null)
+ {
+ synchronized (list)
+ {
+ if (!list.contains(listener))
+ {
+ list.add(status);
+ }
+ }
+ }
+ // report cached service types
+ final List<ServiceEvent> serviceEvents = new ArrayList<ServiceEvent>();
+ Collection<DNSEntry> dnsEntryLits = this.getCache().allValues();
+ for (DNSEntry entry : dnsEntryLits)
+ {
+ final DNSRecord record = (DNSRecord) entry;
+ if (record.getRecordType() == DNSRecordType.TYPE_SRV)
+ {
+ if (record.getName().endsWith(type))
+ {
+ // Do not used the record embedded method for generating event this will not work.
+ // serviceEvents.add(record.getServiceEvent(this));
+ serviceEvents.add(new ServiceEventImpl(this, type, toUnqualifiedName(type, record.getName()), record.getServiceInfo()));
+ }
+ }
+ }
+ // Actually call listener with all service events added above
+ for (ServiceEvent serviceEvent : serviceEvents)
+ {
+ status.serviceAdded(serviceEvent);
+ }
+ // Create/start ServiceResolver
+ new ServiceResolver(this, type).start(_timer);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#removeServiceListener(java.lang.String, javax.jmdns.ServiceListener)
+ */
+ @Override
+ public void removeServiceListener(String type, ServiceListener listener)
+ {
+ String aType = type.toLowerCase();
+ List<ServiceListenerStatus> list = (List<ServiceListenerStatus>) _serviceListeners.get(aType);
+ if (list != null)
+ {
+ synchronized (list)
+ {
+ ServiceListenerStatus status = new ServiceListenerStatus(listener);
+ list.remove(status);
+ if (list.isEmpty())
+ {
+ _serviceListeners.remove(aType, list);
+ }
+ }
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#registerService(javax.jmdns.ServiceInfo)
+ */
+ @Override
+ public void registerService(ServiceInfo infoAbstract) throws IOException
+ {
+ final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
+
+ if ((info.getDns() != null) && (info.getDns() != this))
+ {
+ throw new IllegalStateException("This service information is already registered with another DNS.");
+ }
+ info.setDns(this);
+
+ this.registerServiceType(info.getTypeWithSubtype());
+
+ // bind the service to this address
+ info.setServer(_localHost.getName());
+ info.setAddress(_localHost.getInet4Address());
+ info.setAddress(_localHost.getInet6Address());
+
+ this.waitForAnnounced(0);
+
+ this.makeServiceNameUnique(info);
+ while (_services.putIfAbsent(info.getQualifiedName().toLowerCase(), info) != null)
+ {
+ this.makeServiceNameUnique(info);
+ }
+
+ this.startProber();
+ info.waitForAnnounced(0);
+
+ if (logger.isLoggable(Level.FINE))
+ {
+ logger.fine("registerService() JmDNS registered service as " + info);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#unregisterService(javax.jmdns.ServiceInfo)
+ */
+ @Override
+ public void unregisterService(ServiceInfo infoAbstract)
+ {
+ final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;
+ info.cancelState();
+ this.startCanceler();
+
+ // Remind: We get a deadlock here, if the Canceler does not run!
+ info.waitForCanceled(0);
+
+ _services.remove(info.getQualifiedName().toLowerCase(), info);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#unregisterAllServices()
+ */
+ @Override
+ public void unregisterAllServices()
+ {
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("unregisterAllServices()");
+ }
+
+ for (Iterator<String> itr = _services.keySet().iterator(); itr.hasNext();)
+// for (String name : _services.keySet())
+ {
+ ServiceInfoImpl info = (ServiceInfoImpl) _services.get(itr.next());
+ if (info != null)
+ {
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("Cancelling service info: " + info);
+ }
+ info.cancelState();
+ }
+ }
+ this.startCanceler();
+
+ for (Iterator<String> itr = _services.keySet().iterator(); itr.hasNext();)
+// for (String name : _services.keySet())
+ {
+ final String name = itr.next();
+ ServiceInfoImpl info = (ServiceInfoImpl) _services.get(name);
+ if (info != null)
+ {
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("Wait for service info cancel: " + info);
+ }
+ info.waitForCanceled(DNSConstants.CLOSE_TIMEOUT);
+ _services.remove(name, info);
+ }
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#registerServiceType(java.lang.String)
+ */
+ @Override
+ public boolean registerServiceType(String type)
+ {
+ boolean typeAdded = false;
+ Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(type);
+ String domain = map.get(Fields.Domain);
+ String protocol = map.get(Fields.Protocol);
+ String application = map.get(Fields.Application);
+ String subtype = map.get(Fields.Subtype);
+
+ String name = (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
+ if (logger.isLoggable(Level.FINE))
+ {
+ logger.fine(this.getName() + ".registering service type: " + type + " as: " + name + (subtype.length() > 0 ? " subtype: " + subtype : ""));
+ }
+ if (!_serviceTypes.containsKey(name) && !application.equals("dns-sd") && !domain.endsWith("in-addr.arpa") && !domain.endsWith("ip6.arpa"))
+ {
+ typeAdded = _serviceTypes.putIfAbsent(name, new HashSet<String>()) == null;
+ if (typeAdded)
+ {
+ final ServiceTypeListenerStatus[] list = _typeListeners.toArray(new ServiceTypeListenerStatus[_typeListeners.size()]);
+ final ServiceEvent event = new ServiceEventImpl(this, name, "", null);
+ for (final ServiceTypeListenerStatus status : list)
+ {
+ _executor.submit(new Runnable() {
+ @Override
+ public void run()
+ {
+ status.serviceTypeAdded(event);
+ }
+ });
+ }
+ }
+ }
+ if (subtype.length() > 0)
+ {
+ Set<String> subtypes = (Set<String>) _serviceTypes.get(name);
+ if ((subtypes != null) && (!subtypes.contains(subtype)))
+ {
+ synchronized (subtypes)
+ {
+ if (!subtypes.contains(subtype))
+ {
+ typeAdded = true;
+ subtypes.add(subtype);
+ final ServiceTypeListenerStatus[] list = _typeListeners.toArray(new ServiceTypeListenerStatus[_typeListeners.size()]);
+ final ServiceEvent event = new ServiceEventImpl(this, "_" + subtype + "._sub." + name, "", null);
+ for (final ServiceTypeListenerStatus status : list)
+ {
+ _executor.submit(new Runnable() {
+ @Override
+ public void run()
+ {
+ status.subTypeForServiceTypeAdded(event);
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+ return typeAdded;
+ }
+
+ /**
+ * Generate a possibly unique name for a service using the information we have in the cache.
+ *
+ * @return returns true, if the name of the service info had to be changed.
+ */
+ private boolean makeServiceNameUnique(ServiceInfoImpl info)
+ {
+ final String originalQualifiedName = info.getQualifiedName();
+ final long now = System.currentTimeMillis();
+
+ boolean collision;
+ do
+ {
+ collision = false;
+
+ // Check for collision in cache
+ Collection<? extends DNSEntry> entryList = this.getCache().getDNSEntryList(info.getQualifiedName().toLowerCase());
+ if (entryList != null)
+ {
+ for (DNSEntry dnsEntry : entryList)
+ {
+ if (DNSRecordType.TYPE_SRV.equals(dnsEntry.getRecordType()) && !dnsEntry.isExpired(now))
+ {
+ final DNSRecord.Service s = (DNSRecord.Service) dnsEntry;
+ if (s.getPort() != info.getPort() || !s.getServer().equals(_localHost.getName()))
+ {
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("makeServiceNameUnique() JmDNS.makeServiceNameUnique srv collision:" + dnsEntry + " s.server=" + s.getServer() + " " + _localHost.getName() + " equals:" + (s.getServer().equals(_localHost.getName())));
+ }
+ info.setName(incrementName(info.getName()));
+ collision = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Check for collision with other service infos published by JmDNS
+ final ServiceInfo selfService = (ServiceInfo) _services.get(info.getQualifiedName().toLowerCase());
+ if (selfService != null && selfService != info)
+ {
+ info.setName(incrementName(info.getName()));
+ collision = true;
+ }
+ }
+ while (collision);
+
+ return !(originalQualifiedName.equals(info.getQualifiedName()));
+ }
+
+ String incrementName(String name)
+ {
+ String aName = name;
+ try
+ {
+ final int l = aName.lastIndexOf('(');
+ final int r = aName.lastIndexOf(')');
+ if ((l >= 0) && (l < r))
+ {
+ aName = aName.substring(0, l) + "(" + (Integer.parseInt(aName.substring(l + 1, r)) + 1) + ")";
+ }
+ else
+ {
+ aName += " (2)";
+ }
+ }
+ catch (final NumberFormatException e)
+ {
+ aName += " (2)";
+ }
+ return aName;
+ }
+
+ /**
+ * Add a listener for a question. The listener will receive updates of answers to the question as they arrive, or from the cache if they are already available.
+ *
+ * @param listener
+ * DSN listener
+ * @param question
+ * DNS query
+ */
+ public void addListener(DNSListener listener, DNSQuestion question)
+ {
+ final long now = System.currentTimeMillis();
+
+ // add the new listener
+ _listeners.add(listener);
+
+ // report existing matched records
+
+ if (question != null)
+ {
+ Collection<? extends DNSEntry> entryList = this.getCache().getDNSEntryList(question.getName().toLowerCase());
+ if (entryList != null)
+ {
+ synchronized (entryList)
+ {
+ for (DNSEntry dnsEntry : entryList)
+ {
+ if (question.answeredBy(dnsEntry) && !dnsEntry.isExpired(now))
+ {
+ listener.updateRecord(this.getCache(), now, dnsEntry);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove a listener from all outstanding questions. The listener will no longer receive any updates.
+ *
+ * @param listener
+ * DSN listener
+ */
+ public void removeListener(DNSListener listener)
+ {
+ _listeners.remove(listener);
+ }
+
+ /**
+ * Renew a service when the record become stale. If there is no service collector for the type this method does nothing.
+ *
+ * @param record
+ * DNS record
+ */
+ public void renewServiceCollector(DNSRecord record)
+ {
+ ServiceInfo info = record.getServiceInfo();
+ if (_serviceCollectors.containsKey(info.getType().toLowerCase()))
+ {
+ // Create/start ServiceResolver
+ new ServiceResolver(this, info.getType()).start(_timer);
+ }
+ }
+
+ // Remind: Method updateRecord should receive a better name.
+ /**
+ * Notify all listeners that a record was updated.
+ *
+ * @param now
+ * update date
+ * @param rec
+ * DNS record
+ * @param operation
+ * DNS cache operation
+ */
+ public void updateRecord(long now, DNSRecord rec, Operation operation)
+ {
+ // We do not want to block the entire DNS while we are updating the record for each listener (service info)
+ {
+ List<DNSListener> listenerList = null;
+ synchronized (_listeners)
+ {
+ listenerList = new ArrayList<DNSListener>(_listeners);
+ }
+ for (DNSListener listener : listenerList)
+ {
+ listener.updateRecord(this.getCache(), now, rec);
+ }
+ }
+ if (DNSRecordType.TYPE_PTR.equals(rec.getRecordType()))
+ // if (DNSRecordType.TYPE_PTR.equals(rec.getRecordType()) || DNSRecordType.TYPE_SRV.equals(rec.getRecordType()))
+ {
+ ServiceEvent event = rec.getServiceEvent(this);
+ if ((event.getInfo() == null) || !event.getInfo().hasData())
+ {
+ // We do not care about the subtype because the info is only used if complete and the subtype will then be included.
+ ServiceInfo info = this.getServiceInfoFromCache(event.getType(), event.getName(), "", false);
+ if (info.hasData())
+ {
+ event = new ServiceEventImpl(this, event.getType(), event.getName(), info);
+ }
+ }
+
+ List<ServiceListenerStatus> list = (List<ServiceListenerStatus>) _serviceListeners.get(event.getType());
+ final List<ServiceListenerStatus> serviceListenerList;
+ if (list != null)
+ {
+ synchronized (list)
+ {
+ serviceListenerList = new ArrayList<ServiceListenerStatus>(list);
+ }
+ }
+ else
+ {
+ serviceListenerList = Collections.emptyList();
+ }
+ if (logger.isLoggable(Level.FINEST))
+ {
+ logger.finest(this.getName() + ".updating record for event: " + event + " list " + serviceListenerList + " operation: " + operation);
+ }
+ if (!serviceListenerList.isEmpty())
+ {
+ final ServiceEvent localEvent = event;
+
+ switch (operation)
+ {
+ case Add:
+ for (final ServiceListenerStatus listener : serviceListenerList)
+ {
+ _executor.submit(new Runnable() {
+ @Override
+ public void run()
+ {
+ listener.serviceAdded(localEvent);
+ }
+ });
+ }
+ break;
+ case Remove:
+ for (final ServiceListenerStatus listener : serviceListenerList)
+ {
+ _executor.submit(new Runnable() {
+ @Override
+ public void run()
+ {
+ listener.serviceRemoved(localEvent);
+ }
+ });
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ void handleRecord(DNSRecord record, long now)
+ {
+ DNSRecord newRecord = record;
+
+ Operation cacheOperation = Operation.Noop;
+ final boolean expired = newRecord.isExpired(now);
+
+ // update the cache
+ final DNSRecord cachedRecord = (DNSRecord) this.getCache().getDNSEntry(newRecord);
+ if (logger.isLoggable(Level.FINE))
+ {
+ logger.fine(this.getName() + ".handle response: " + newRecord + "\ncached record: " + cachedRecord);
+ }
+ if (cachedRecord != null)
+ {
+ if (expired)
+ {
+ cacheOperation = Operation.Remove;
+ this.getCache().removeDNSEntry(cachedRecord);
+ }
+ else
+ {
+ // If the record content has changed we need to inform our listeners.
+ if (!newRecord.sameValue(cachedRecord) || (!newRecord.sameSubtype(cachedRecord) && (newRecord.getSubtype().length() > 0)))
+ {
+ cacheOperation = Operation.Update;
+ this.getCache().replaceDNSEntry(newRecord, cachedRecord);
+ }
+ else
+ {
+ cachedRecord.resetTTL(newRecord);
+ newRecord = cachedRecord;
+ }
+ }
+ }
+ else
+ {
+ if (!expired)
+ {
+ cacheOperation = Operation.Add;
+ this.getCache().addDNSEntry(newRecord);
+ }
+ }
+
+ switch (newRecord.getRecordType())
+ {
+ case TYPE_PTR:
+ // handle DNSConstants.DNS_META_QUERY records
+ boolean typeAdded = false;
+ if (newRecord.isServicesDiscoveryMetaQuery())
+ {
+ // The service names are in the alias.
+ if (!expired)
+ {
+ typeAdded = this.registerServiceType(((DNSRecord.Pointer) newRecord).getAlias());
+ }
+ return;
+ }
+ typeAdded |= this.registerServiceType(newRecord.getName());
+ if (typeAdded && (cacheOperation == Operation.Noop))
+ cacheOperation = Operation.RegisterServiceType;
+ break;
+ default:
+ break;
+ }
+
+ // notify the listeners
+ if (cacheOperation != Operation.Noop)
+ {
+ this.updateRecord(now, newRecord, cacheOperation);
+ }
+
+ }
+
+ /**
+ * Handle an incoming response. Cache answers, and pass them on to the appropriate questions.
+ *
+ * @throws IOException
+ */
+ void handleResponse(DNSIncoming msg) throws IOException
+ {
+ final long now = System.currentTimeMillis();
+
+ boolean hostConflictDetected = false;
+ boolean serviceConflictDetected = false;
+
+ for (Iterator itr = msg.getAllAnswers().iterator(); itr.hasNext();)
+// for (DNSRecord newRecord : msg.getAllAnswers())
+ {
+ final DNSRecord newRecord = (DNSRecord) itr.next();
+ this.handleRecord(newRecord, now);
+
+ if (DNSRecordType.TYPE_A.equals(newRecord.getRecordType()) || DNSRecordType.TYPE_AAAA.equals(newRecord.getRecordType()))
+ {
+ hostConflictDetected |= newRecord.handleResponse(this);
+ }
+ else
+ {
+ serviceConflictDetected |= newRecord.handleResponse(this);
+ }
+
+ }
+
+ if (hostConflictDetected || serviceConflictDetected)
+ {
+ this.startProber();
+ }
+ }
+
+ /**
+ * Handle an incoming query. See if we can answer any part of it given our service infos.
+ *
+ * @param in
+ * @param addr
+ * @param port
+ * @throws IOException
+ */
+ void handleQuery(DNSIncoming in, InetAddress addr, int port) throws IOException
+ {
+ if (logger.isLoggable(Level.FINE))
+ {
+ logger.fine(this.getName() + ".handle query: " + in);
+ }
+ // Track known answers
+ boolean conflictDetected = false;
+ final long expirationTime = System.currentTimeMillis() + DNSConstants.KNOWN_ANSWER_TTL;
+ for (Iterator itr = in.getAllAnswers().iterator(); itr.hasNext();)
+// for (DNSRecord answer : in.getAllAnswers())
+ {
+ conflictDetected |= ((DNSRecord) itr.next()).handleQuery(this, expirationTime);
+ }
+
+ _ioLock.lock();
+ try
+ {
+
+ if (_plannedAnswer != null)
+ {
+ _plannedAnswer.append(in);
+ }
+ else
+ {
+ if (in.isTruncated())
+ {
+ _plannedAnswer = in;
+ }
+ new Responder(this, in, port).start(_timer);
+ }
+
+ }
+ finally
+ {
+ _ioLock.unlock();
+ }
+
+ final long now = System.currentTimeMillis();
+ for (Iterator itr = in.getAnswers().iterator(); itr.hasNext();)
+// for (DNSRecord answer : in.getAnswers())
+ {
+ this.handleRecord((DNSRecord) itr.next(), now);
+ }
+
+ if (conflictDetected)
+ {
+ this.startProber();
+ }
+ }
+
+ public void respondToQuery(DNSIncoming in)
+ {
+ _ioLock.lock();
+ try
+ {
+ if (_plannedAnswer == in)
+ {
+ _plannedAnswer = null;
+ }
+ }
+ finally
+ {
+ _ioLock.unlock();
+ }
+ }
+
+ /**
+ * Add an answer to a question. Deal with the case when the outgoing packet overflows
+ *
+ * @param in
+ * @param addr
+ * @param port
+ * @param out
+ * @param rec
+ * @return outgoing answer
+ * @throws IOException
+ */
+ public DNSOutgoing addAnswer(DNSIncoming in, InetAddress addr, int port, DNSOutgoing out, DNSRecord rec) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ if (newOut == null)
+ {
+ newOut = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, false, in.getSenderUDPPayload());
+ }
+ try
+ {
+ newOut.addAnswer(in, rec);
+ }
+ catch (final IOException e)
+ {
+ newOut.setFlags(newOut.getFlags() | DNSConstants.FLAGS_TC);
+ newOut.setId(in.getId());
+ send(newOut);
+
+ newOut = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, false, in.getSenderUDPPayload());
+ newOut.addAnswer(in, rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Send an outgoing multicast DNS message.
+ *
+ * @param out
+ * @throws IOException
+ */
+ public void send(DNSOutgoing out) throws IOException
+ {
+ if (!out.isEmpty())
+ {
+ byte[] message = out.data();
+ final DatagramPacket packet = new DatagramPacket(message, message.length, _group, DNSConstants.MDNS_PORT);
+
+ if (logger.isLoggable(Level.FINEST))
+ {
+ try
+ {
+ final DNSIncoming msg = new DNSIncoming(packet);
+ if (logger.isLoggable(Level.FINEST))
+ {
+ logger.finest("send(" + this.getName() + ") JmDNS out:" + msg.print(true));
+ }
+ }
+ catch (final IOException e)
+ {
+ logger.throwing(getClass().toString(), "send(" + this.getName() + ") - JmDNS can not parse what it sends!!!", e);
+ }
+ }
+ final MulticastSocket ms = _socket;
+ if (ms != null && !ms.isClosed())
+ ms.send(packet);
+ }
+ }
+
+ public void startProber()
+ {
+ new Prober(this).start(_stateTimer);
+ }
+
+ public void startAnnouncer()
+ {
+ new Announcer(this).start(_stateTimer);
+ }
+
+ public void startRenewer()
+ {
+ new Renewer(this).start(_stateTimer);
+ }
+
+ public void startCanceler()
+ {
+ new Canceler(this).start(_stateTimer);
+ }
+
+ // REMIND: Why is this not an anonymous inner class?
+ /**
+ * Shutdown operations.
+ */
+ protected class Shutdown implements Runnable
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ _shutdown = null;
+ close();
+ }
+ catch (Throwable exception)
+ {
+ System.err.println("Error while shuting down. " + exception);
+ }
+ }
+ }
+
+ /**
+ * Recover jmdns when there is an error.
+ */
+ public void recover()
+ {
+ logger.finer("recover()");
+ // We have an IO error so lets try to recover if anything happens lets close it.
+ // This should cover the case of the IP address changing under our feet
+ if (this.isCanceling() || this.isCanceled())
+ return;
+
+ // Stop JmDNS
+ // This protects against recursive calls
+ if (this.cancelState())
+ {
+ // Synchronize only if we are not already in process to prevent dead locks
+ //
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("recover() Cleanning up");
+ }
+
+ // Purge the timer
+ _timer.purge();
+
+ // We need to keep a copy for reregistration
+ final Collection<ServiceInfo> oldServiceInfos = new ArrayList<ServiceInfo>(getServices().values());
+
+ // Cancel all services
+ this.unregisterAllServices();
+ this.disposeServiceCollectors();
+
+ this.waitForCanceled(0);
+
+ // Purge the canceler timer
+ _stateTimer.purge();
+
+ //
+ // close multicast socket
+ this.closeMulticastSocket();
+ //
+ this.getCache().clear();
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("recover() All is clean");
+ }
+ //
+ // All is clear now start the services
+ //
+ for (ServiceInfo info : oldServiceInfos)
+ {
+ ((ServiceInfoImpl) info).recoverState();
+ }
+ this.recoverState();
+
+ try
+ {
+ this.openMulticastSocket(this.getLocalHost());
+ this.start(oldServiceInfos);
+ }
+ catch (final Exception exception)
+ {
+ logger.log(Level.WARNING, "recover() Start services exception ", exception);
+ }
+ logger.log(Level.WARNING, "recover() We are back!");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#close()
+ */
+ @Override
+ public void close()
+ {
+ if (this.isCanceling() || this.isCanceled())
+ return;
+
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("Cancelling JmDNS: " + this);
+ }
+ // Stop JmDNS
+ // This protects against recursive calls
+ if (this.cancelState())
+ {
+ // We got the tie break now clean up
+
+ // Stop the timer
+ _timer.cancel();
+
+ // Cancel all services
+ this.unregisterAllServices();
+ this.disposeServiceCollectors();
+
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("Wait for JmDNS cancel: " + this);
+ }
+ this.waitForCanceled(DNSConstants.CLOSE_TIMEOUT);
+
+ // Stop the canceler timer
+ _stateTimer.cancel();
+
+ // Stop the executor
+ _executor.shutdown();
+
+ // close socket
+ this.closeMulticastSocket();
+
+ // remove the shutdown hook
+ if (_shutdown != null)
+ {
+ Runtime.getRuntime().removeShutdownHook(_shutdown);
+ }
+
+ }
+ }
+
+ /**
+ * @see javax.jmdns.JmDNS#printServices()
+ */
+ @Override
+ public void printServices()
+ {
+ System.err.println(toString());
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder aLog = new StringBuilder(2048);
+ aLog.append("\t---- Local Host -----");
+ aLog.append("\n\t" + _localHost);
+ aLog.append("\n\t---- Services -----");
+ for (Iterator<String> itr = _services.keySet().iterator(); itr.hasNext();)
+// for (String key : _services.keySet())
+ {
+ final String key = itr.next();
+ aLog.append("\n\t\tService: " + key + ": " + _services.get(key));
+ }
+ aLog.append("\n");
+ aLog.append("\t---- Types ----");
+ for (Iterator<String> itr = _serviceTypes.keySet().iterator(); itr.hasNext();)
+// for (String key : _serviceTypes.keySet())
+ {
+ final String key = itr.next();
+ Set<String> subtypes = (Set<String>) _serviceTypes.get(key);
+ aLog.append("\n\t\tType: " + key + ": " + ((subtypes == null) || subtypes.isEmpty() ? "no subtypes" : subtypes));
+ }
+ aLog.append("\n");
+ aLog.append(_cache.toString());
+ aLog.append("\n");
+ aLog.append("\t---- Service Collectors ----");
+ for (Iterator<String> itr = _serviceCollectors.keySet().iterator(); itr.hasNext();)
+// for (String key : _serviceCollectors.keySet())
+ {
+ final String key = itr.next();
+ aLog.append("\n\t\tService Collector: " + key + ": " + _serviceCollectors.get(key));
+ }
+ aLog.append("\n");
+ aLog.append("\t---- Service Listeners ----");
+ for (Iterator<String> itr = _serviceListeners.keySet().iterator(); itr.hasNext();)
+// for (String key : _serviceListeners.keySet())
+ {
+ final String key = itr.next();
+ aLog.append("\n\t\tService Listener: " + key + ": " + _serviceListeners.get(key));
+ }
+ return aLog.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#list(java.lang.String)
+ */
+ @Override
+ public ServiceInfo[] list(String type)
+ {
+ return this.list(type, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#list(java.lang.String, int)
+ */
+ @Override
+ public ServiceInfo[] list(String type, long timeout)
+ {
+ // Implementation note: The first time a list for a given type is
+ // requested, a ServiceCollector is created which collects service
+ // infos. This greatly speeds up the performance of subsequent calls
+ // to this method. The caveats are, that 1) the first call to this
+ // method for a given type is slow, and 2) we spawn a ServiceCollector
+ // instance for each service type which increases network traffic a
+ // little.
+
+ String aType = type.toLowerCase();
+
+ boolean newCollectorCreated = false;
+ if (this.isCanceling() || this.isCanceled())
+ {
+ return new ServiceInfo[0];
+ }
+
+ ServiceCollector collector = (ServiceCollector) _serviceCollectors.get(aType);
+ if (collector == null)
+ {
+ newCollectorCreated = _serviceCollectors.putIfAbsent(aType, new ServiceCollector(aType)) == null;
+ collector = (ServiceCollector) _serviceCollectors.get(aType);
+ if (newCollectorCreated)
+ {
+ this.addServiceListener(aType, collector);
+ }
+ }
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer(this.getName() + ".collector: " + collector);
+ }
+ // At this stage the collector should never be null but it keeps findbugs happy.
+ return (collector != null ? collector.list(timeout) : new ServiceInfo[0]);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#listBySubtype(java.lang.String)
+ */
+ @Override
+ public Map<String, ServiceInfo[]> listBySubtype(String type)
+ {
+ return this.listBySubtype(type, DNSConstants.SERVICE_INFO_TIMEOUT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.JmDNS#listBySubtype(java.lang.String, long)
+ */
+ @Override
+ public Map<String, ServiceInfo[]> listBySubtype(String type, long timeout)
+ {
+ Map<String, List<ServiceInfo>> map = new HashMap<String, List<ServiceInfo>>(5);
+ for (ServiceInfo info : this.list(type, timeout))
+ {
+ String subtype = info.getSubtype();
+ if (!map.containsKey(subtype))
+ {
+ map.put(subtype, new ArrayList<ServiceInfo>(10));
+ }
+ map.get(subtype).add(info);
+ }
+
+ Map<String, ServiceInfo[]> result = new HashMap<String, ServiceInfo[]>(map.size());
+ for (String subtype : map.keySet())
+ {
+ List<ServiceInfo> infoForSubType = map.get(subtype);
+ result.put(subtype, infoForSubType.toArray(new ServiceInfo[infoForSubType.size()]));
+ }
+
+ return result;
+ }
+
+ /**
+ * This method disposes all ServiceCollector instances which have been created by calls to method <code>list(type)</code>.
+ *
+ * @see #list
+ */
+ private void disposeServiceCollectors()
+ {
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer("disposeServiceCollectors()");
+ }
+ for (Iterator<String> itr = _serviceCollectors.keySet().iterator(); itr.hasNext();)
+// for (String type : _serviceCollectors.keySet())
+ {
+ final String type = itr.next();
+ ServiceCollector collector = (ServiceCollector) _serviceCollectors.get(type);
+ if (collector != null)
+ {
+ this.removeServiceListener(type, collector);
+ _serviceCollectors.remove(type, collector);
+ }
+ }
+ }
+
+ /**
+ * Instances of ServiceCollector are used internally to speed up the performance of method <code>list(type)</code>.
+ *
+ * @see #list
+ */
+ private static class ServiceCollector implements ServiceListener
+ {
+ // private static Logger logger = Logger.getLogger(ServiceCollector.class.getName());
+
+ /**
+ * A set of collected service instance names.
+ */
+ private final ConcurrentMap/*<String, ServiceInfo>*/ _infos;
+
+ /**
+ * A set of collected service event waiting to be resolved.
+ */
+ private final ConcurrentMap/*<String, ServiceEvent>*/ _events;
+
+ private final String _type;
+
+ /**
+ * This is used to force a wait on the first invocation of list.
+ */
+ private volatile boolean _needToWaitForInfos;
+
+ public ServiceCollector(String type)
+ {
+ super();
+ _infos = new ConcurrentHashMap/*<String, ServiceInfo>*/();
+ _events = new ConcurrentHashMap/*<String, ServiceEvent>*/();
+ _type = type;
+ _needToWaitForInfos = true;
+ }
+
+ /**
+ * A service has been added.
+ *
+ * @param event
+ * service event
+ */
+ @Override
+ public void serviceAdded(ServiceEvent event)
+ {
+ synchronized (this)
+ {
+ ServiceInfo info = event.getInfo();
+ if ((info != null) && (info.hasData()))
+ {
+ _infos.put(event.getName(), info);
+ }
+ else
+ {
+ String subtype = (info != null ? info.getSubtype() : "");
+ info = ((JmDNSImpl) event.getDNS()).resolveServiceInfo(event.getType(), event.getName(), subtype, true);
+ if (info != null)
+ {
+ _infos.put(event.getName(), info);
+ }
+ else
+ {
+ _events.put(event.getName(), event);
+ }
+ }
+ }
+ }
+
+ /**
+ * A service has been removed.
+ *
+ * @param event
+ * service event
+ */
+ @Override
+ public void serviceRemoved(ServiceEvent event)
+ {
+ synchronized (this)
+ {
+ _infos.remove(event.getName());
+ _events.remove(event.getName());
+ }
+ }
+
+ /**
+ * A service has been resolved. Its details are now available in the ServiceInfo record.
+ *
+ * @param event
+ * service event
+ */
+ @Override
+ public void serviceResolved(ServiceEvent event)
+ {
+ synchronized (this)
+ {
+ _infos.put(event.getName(), event.getInfo());
+ _events.remove(event.getName());
+ }
+ }
+
+ /**
+ * Returns an array of all service infos which have been collected by this ServiceCollector.
+ *
+ * @param timeout
+ * timeout if the info list is empty.
+ *
+ * @return Service Info array
+ */
+ public ServiceInfo[] list(long timeout)
+ {
+ if (_infos.isEmpty() || !_events.isEmpty() || _needToWaitForInfos)
+ {
+ long loops = (timeout / 200L);
+ if (loops < 1)
+ {
+ loops = 1;
+ }
+ for (int i = 0; i < loops; i++)
+ {
+ try
+ {
+ Thread.sleep(200);
+ }
+ catch (final InterruptedException e)
+ {
+ /* Stub */
+ }
+ if (_events.isEmpty() && !_infos.isEmpty() && !_needToWaitForInfos)
+ {
+ break;
+ }
+ }
+ }
+ _needToWaitForInfos = false;
+ return (ServiceInfo[]) _infos.values().toArray(new ServiceInfo[_infos.size()]);
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuffer aLog = new StringBuffer();
+ aLog.append("\n\tType: " + _type);
+ if (_infos.isEmpty())
+ {
+ aLog.append("\n\tNo services collected.");
+ }
+ else
+ {
+ aLog.append("\n\tServices");
+ for (Iterator<String> itr = _infos.keySet().iterator(); itr.hasNext();)
+// for (String key : _infos.keySet())
+ {
+ final String key = itr.next();
+ aLog.append("\n\t\tService: " + key + ": " + _infos.get(key));
+ }
+ }
+ if (_events.isEmpty())
+ {
+ aLog.append("\n\tNo event queued.");
+ }
+ else
+ {
+ aLog.append("\n\tEvents");
+ for (Iterator<String> itr = _events.keySet().iterator(); itr.hasNext();)
+// for (String key : _events.keySet())
+ {
+ final String key = itr.next();
+ aLog.append("\n\t\tEvent: " + key + ": " + _events.get(key));
+ }
+ }
+ return aLog.toString();
+ }
+ }
+
+ static String toUnqualifiedName(String type, String qualifiedName)
+ {
+ if (qualifiedName.endsWith(type) && !(qualifiedName.equals(type)))
+ {
+ return qualifiedName.substring(0, qualifiedName.length() - type.length() - 1);
+ }
+ return qualifiedName;
+ }
+
+ public Map<String, ServiceInfo> getServices()
+ {
+ return _services;
+ }
+
+ public void setLastThrottleIncrement(long lastThrottleIncrement)
+ {
+ this._lastThrottleIncrement = lastThrottleIncrement;
+ }
+
+ public long getLastThrottleIncrement()
+ {
+ return _lastThrottleIncrement;
+ }
+
+ public void setThrottle(int throttle)
+ {
+ this._throttle = throttle;
+ }
+
+ public int getThrottle()
+ {
+ return _throttle;
+ }
+
+ public static Random getRandom()
+ {
+ return _random;
+ }
+
+ public void ioLock()
+ {
+ _ioLock.lock();
+ }
+
+ public void ioUnlock()
+ {
+ _ioLock.unlock();
+ }
+
+ public void setPlannedAnswer(DNSIncoming plannedAnswer)
+ {
+ this._plannedAnswer = plannedAnswer;
+ }
+
+ public DNSIncoming getPlannedAnswer()
+ {
+ return _plannedAnswer;
+ }
+
+ void setLocalHost(HostInfo localHost)
+ {
+ this._localHost = localHost;
+ }
+
+ public Map<String, Set<String>> getServiceTypes()
+ {
+ return _serviceTypes;
+ }
+
+ public void setClosed(boolean closed)
+ {
+ this._closed = closed;
+ }
+
+ public boolean isClosed()
+ {
+ return _closed;
+ }
+
+ public MulticastSocket getSocket()
+ {
+ return _socket;
+ }
+
+ public InetAddress getGroup()
+ {
+ return _group;
+ }
+
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ListenerStatus.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ListenerStatus.java
new file mode 100644
index 000000000..96cbb9c35
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ListenerStatus.java
@@ -0,0 +1,330 @@
+/**
+ *
+ */
+package javax.jmdns.impl;
+
+import java.util.Iterator;
+
+import java.util.EventListener;
+import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
+import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
+import java.util.logging.Logger;
+
+import javax.jmdns.JmDNS;
+import javax.jmdns.ServiceEvent;
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.ServiceListener;
+import javax.jmdns.ServiceTypeListener;
+
+/**
+ * This class track the status of listener.<br/>
+ * The main purpose of this class is to collapse consecutive events so that we can guarantee the correct call back sequence.
+ *
+ * @param <T>
+ * listener type
+ *
+ */
+public class ListenerStatus<T extends EventListener>
+{
+
+ public static class ServiceListenerStatus extends ListenerStatus<ServiceListener>
+ {
+ private static Logger logger = Logger.getLogger(ServiceListenerStatus.class.getName());
+
+ private final ConcurrentMap/*<String, ServiceInfo>*/ _addedServices;
+
+ /**
+ * @param listener
+ */
+ public ServiceListenerStatus(ServiceListener listener)
+ {
+ super(listener);
+ _addedServices = new ConcurrentHashMap/*<String, ServiceInfo>*/(32);
+ }
+
+ /**
+ * A service has been added.<br/>
+ * <b>Note:</b>This event is only the service added event. The service info associated with this event does not include resolution information.<br/>
+ * To get the full resolved information you need to listen to {@link #serviceResolved(ServiceEvent)} or call {@link JmDNS#getServiceInfo(String, String, long)}
+ *
+ * <pre>
+ * ServiceInfo info = event.getDNS().getServiceInfo(event.getType(), event.getName())
+ * </pre>
+ * <p>
+ * Please note that service resolution may take a few second to resolve.
+ * </p>
+ *
+ * @param event
+ * The ServiceEvent providing the name and fully qualified type of the service.
+ */
+ void serviceAdded(ServiceEvent event)
+ {
+ String qualifiedName = event.getName() + "." + event.getType();
+ if (null == _addedServices.putIfAbsent(qualifiedName, (ServiceInfo) event.getInfo().clone()))
+ {
+ this.getListener().serviceAdded(event);
+ ServiceInfo info = event.getInfo();
+ if ((info != null) && (info.hasData()))
+ {
+ this.getListener().serviceResolved(event);
+ }
+ }
+ else
+ {
+ logger.finer("Service Added called for a service already added: " + event);
+ }
+ }
+
+ /**
+ * A service has been removed.
+ *
+ * @param event
+ * The ServiceEvent providing the name and fully qualified type of the service.
+ */
+ void serviceRemoved(ServiceEvent event)
+ {
+ String qualifiedName = event.getName() + "." + event.getType();
+ if (_addedServices.remove(qualifiedName, _addedServices.get(qualifiedName)))
+ {
+ this.getListener().serviceRemoved(event);
+ }
+ else
+ {
+ logger.finer("Service Removed called for a service already removed: " + event);
+ }
+ }
+
+ /**
+ * A service has been resolved. Its details are now available in the ServiceInfo record.<br/>
+ * <b>Note:</b>This call back will never be called if the service does not resolve.<br/>
+ *
+ * @param event
+ * The ServiceEvent providing the name, the fully qualified type of the service, and the service info record.
+ */
+ synchronized void serviceResolved(ServiceEvent event)
+ {
+ ServiceInfo info = event.getInfo();
+ if ((info != null) && (info.hasData()))
+ {
+ String qualifiedName = event.getName() + "." + event.getType();
+ ServiceInfo previousServiceInfo = (ServiceInfo) _addedServices.get(qualifiedName);
+ if (!_sameInfo(info, previousServiceInfo))
+ {
+ if (null == previousServiceInfo)
+ {
+ if (null == _addedServices.putIfAbsent(qualifiedName, (ServiceInfo) info.clone()))
+ {
+ this.getListener().serviceResolved(event);
+ }
+ }
+ else
+ {
+ if (_addedServices.replace(qualifiedName, previousServiceInfo, (ServiceInfo) info.clone()))
+ {
+ this.getListener().serviceResolved(event);
+ }
+ }
+ }
+ else
+ {
+ logger.finer("Service Resolved called for a service already resolved: " + event);
+ }
+ }
+ else
+ {
+ logger.warning("Service Resolved called for an unresolved event: " + event);
+
+ }
+ }
+
+ private static final boolean _sameInfo(ServiceInfo info, ServiceInfo lastInfo)
+ {
+ if (info == null)
+ return false;
+ if (lastInfo == null)
+ return false;
+ if (!info.equals(lastInfo))
+ return false;
+ byte[] text = info.getTextBytes();
+ byte[] lastText = lastInfo.getTextBytes();
+ if (text.length != lastText.length)
+ return false;
+ for (int i = 0; i < text.length; i++)
+ {
+ if (text[i] != lastText[i])
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder aLog = new StringBuilder(2048);
+ aLog.append("[Status for ");
+ aLog.append(this.getListener().toString());
+ if (_addedServices.isEmpty())
+ {
+ aLog.append(" no type event ");
+ }
+ else
+ {
+ aLog.append(" (");
+ for (Iterator itr = _addedServices.keySet().iterator(); itr.hasNext();)
+// for (String service : _addedServices.keySet())
+ {
+ aLog.append(itr.next() + ", ");
+ }
+ aLog.append(") ");
+ }
+ aLog.append("]");
+ return aLog.toString();
+ }
+
+ }
+
+ public static class ServiceTypeListenerStatus extends ListenerStatus<ServiceTypeListener>
+ {
+ private static Logger logger = Logger.getLogger(ServiceTypeListenerStatus.class.getName());
+
+ private final ConcurrentMap/*<String, String>*/ _addedTypes;
+
+ /**
+ * @param listener
+ */
+ public ServiceTypeListenerStatus(ServiceTypeListener listener)
+ {
+ super(listener);
+ _addedTypes = new ConcurrentHashMap/*<String, String>*/(32);
+ }
+
+ /**
+ * A new service type was discovered.
+ *
+ * @param event
+ * The service event providing the fully qualified type of the service.
+ */
+ void serviceTypeAdded(ServiceEvent event)
+ {
+ if (null == _addedTypes.putIfAbsent(event.getType(), event.getType()))
+ {
+ this.getListener().serviceTypeAdded(event);
+ }
+ else
+ {
+ logger.finest("Service Type Added called for a service type already added: " + event);
+ }
+ }
+
+ /**
+ * A new subtype for the service type was discovered.
+ *
+ * <pre>
+ * &lt;sub&gt;._sub.&lt;app&gt;.&lt;protocol&gt;.&lt;servicedomain&gt;.&lt;parentdomain&gt;.
+ * </pre>
+ *
+ * @param event
+ * The service event providing the fully qualified type of the service with subtype.
+ */
+ void subTypeForServiceTypeAdded(ServiceEvent event)
+ {
+ if (null == _addedTypes.putIfAbsent(event.getType(), event.getType()))
+ {
+ this.getListener().subTypeForServiceTypeAdded(event);
+ }
+ else
+ {
+ logger.finest("Service Sub Type Added called for a service sub type already added: " + event);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder aLog = new StringBuilder(2048);
+ aLog.append("[Status for ");
+ aLog.append(this.getListener().toString());
+ if (_addedTypes.isEmpty())
+ {
+ aLog.append(" no type event ");
+ }
+ else
+ {
+ aLog.append(" (");
+ for (Iterator itr = _addedTypes.keySet().iterator(); itr.hasNext();)
+// for (String type : _addedTypes.keySet())
+ {
+ aLog.append(itr.next() + ", ");
+ }
+ aLog.append(") ");
+ }
+ aLog.append("]");
+ return aLog.toString();
+ }
+
+ }
+
+ private final T _listener;
+
+ /**
+ * @param listener
+ * listener being tracked.
+ *
+ */
+ public ListenerStatus(T listener)
+ {
+ super();
+ _listener = listener;
+ }
+
+ /**
+ * @return the listener
+ */
+ public T getListener()
+ {
+ return _listener;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode()
+ {
+ return this.getListener().hashCode();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ return (obj instanceof ListenerStatus) && this.getListener().equals(((ListenerStatus<?>) obj).getListener());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return "[Status for " + this.getListener().toString() + "]";
+ }
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceEventImpl.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceEventImpl.java
index df8ae2f10..f925e0cae 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceEventImpl.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceEventImpl.java
@@ -4,8 +4,6 @@
package javax.jmdns.impl;
-//import java.util.logging.Logger;
-
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
@@ -18,41 +16,48 @@ import javax.jmdns.ServiceInfo;
*/
public class ServiceEventImpl extends ServiceEvent
{
-// private static Logger logger = Logger.getLogger(ServiceEvent.class.getName());
+ /**
+ *
+ */
+ private static final long serialVersionUID = 7107973622016897488L;
+ // private static Logger logger = Logger.getLogger(ServiceEvent.class.getName());
/**
* The type name of the service.
*/
- private String type;
+ private String _type;
/**
- * The instance name of the service. Or null, if the event was
- * fired to a service type listener.
+ * The instance name of the service. Or null, if the event was fired to a service type listener.
*/
- private String name;
+ private String _name;
/**
- * The service info record, or null if the service could be be resolved.
- * This is also null, if the event was fired to a service type listener.
+ * The service info record, or null if the service could be be resolved. This is also null, if the event was fired to a service type listener.
*/
- private ServiceInfoImpl info;
+ private ServiceInfo _info;
/**
* Creates a new instance.
*
- * @param source the JmDNS instance which originated the event.
- * @param type the type name of the service.
- * @param name the instance name of the service.
- * @param info the service info record, or null if the service could be be resolved.
+ * @param jmDNS
+ * the JmDNS instance which originated the event.
+ * @param type
+ * the type name of the service.
+ * @param name
+ * the instance name of the service.
+ * @param info
+ * the service info record, or null if the service could be be resolved.
*/
- public ServiceEventImpl(JmDNSImpl source, String type, String name, ServiceInfoImpl info)
+ public ServiceEventImpl(JmDNSImpl jmDNS, String type, String name, ServiceInfo info)
{
- super(source);
- this.type = type;
- this.name = name;
- this.info = info;
+ super(jmDNS);
+ this._type = type;
+ this._name = name;
+ this._info = info;
}
/**
* @see javax.jmdns.ServiceEvent#getDNS()
*/
+ @Override
public JmDNS getDNS()
{
return (JmDNS) getSource();
@@ -61,39 +66,58 @@ public class ServiceEventImpl extends ServiceEvent
/**
* @see javax.jmdns.ServiceEvent#getType()
*/
+ @Override
public String getType()
{
- return type;
+ return _type;
}
/**
* @see javax.jmdns.ServiceEvent#getName()
*/
+ @Override
public String getName()
{
- return name;
+ return _name;
}
/**
* @see javax.jmdns.ServiceEvent#toString()
*/
+ @Override
public String toString()
{
- StringBuffer buf = new StringBuffer();
- buf.append("<" + getClass().getName() + "> ");
- buf.append(super.toString());
- buf.append(" name ");
- buf.append(getName());
- buf.append(" type ");
- buf.append(getType());
- buf.append(" info ");
- buf.append(getInfo());
+ StringBuilder buf = new StringBuilder();
+ buf.append("[" + this.getClass().getSimpleName() + "@" + System.identityHashCode(this) + " ");
+ buf.append("\n\tname: '");
+ buf.append(this.getName());
+ buf.append("' type: '");
+ buf.append(this.getType());
+ buf.append("' info: '");
+ buf.append(this.getInfo());
+ buf.append("']");
+ // buf.append("' source: ");
+ // buf.append("\n\t" + source + "");
+ // buf.append("\n]");
return buf.toString();
}
+ @Override
public ServiceInfo getInfo()
{
- return info;
+ return _info;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone() throws CloneNotSupportedException
+ {
+ ServiceInfoImpl newInfo = new ServiceInfoImpl(this.getInfo());
+ return new ServiceEventImpl((JmDNSImpl) this.getDNS(), this.getType(), this.getName(), newInfo);
}
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceInfoImpl.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceInfoImpl.java
index dda46d1c5..962c2e828 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceInfoImpl.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/ServiceInfoImpl.java
@@ -7,78 +7,184 @@ package javax.jmdns.impl;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
+import java.util.HashMap;
import java.util.Hashtable;
-import java.util.TimerTask;
+import java.util.List;
+import java.util.Map;
import java.util.Vector;
-//import java.util.logging.Logger;
+import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.impl.DNSRecord.Pointer;
import javax.jmdns.impl.DNSRecord.Service;
import javax.jmdns.impl.DNSRecord.Text;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+import javax.jmdns.impl.constants.DNSState;
+import javax.jmdns.impl.tasks.DNSTask;
/**
* JmDNS service information.
*
* @version %I%, %G%
- * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer
*/
-public class ServiceInfoImpl extends ServiceInfo implements DNSListener
+public class ServiceInfoImpl extends ServiceInfo implements DNSListener, Cloneable, DNSStatefulObject
{
-// private static Logger logger = Logger.getLogger(ServiceInfoImpl.class.getName());
- private JmDNSImpl dns;
-
- // State machine
- /**
- * The state of this service info.
- * This is used only for services announced by JmDNS.
- * <p/>
- * For proper handling of concurrency, this variable must be
- * changed only using methods advanceState(), revertState() and cancel().
- */
- private DNSState state = DNSState.PROBING_1;
+ // private static Logger logger = Logger.getLogger(ServiceInfoImpl.class.getName());
+
+ private String _domain;
+ private String _protocol;
+ private String _application;
+ private String _name;
+ private String _subtype;
+ private String _server;
+ private int _port;
+ private int _weight;
+ private int _priority;
+ private byte _text[];
+ private Map<String, byte[]> _props;
+ private Inet4Address _ipv4Addr;
+ private Inet6Address _ipv6Addr;
+
+ private boolean _persistent;
+ private boolean _needTextAnnouncing;
+
+ private final ServiceInfoState _state;
+
+ private final static class ServiceInfoState extends DNSStatefulObject.DefaultImplementation
+ {
+
+ private static final long serialVersionUID = 1104131034952196820L;
+
+ private final ServiceInfoImpl _info;
+
+ /**
+ * @param info
+ */
+ public ServiceInfoState(ServiceInfoImpl info)
+ {
+ super();
+ _info = info;
+ }
+
+ @Override
+ protected void setTask(DNSTask task)
+ {
+ super.setTask(task);
+ if ((this._task == null) && _info.needTextAnnouncing())
+ {
+ this.lock();
+ try
+ {
+ if ((this._task == null) && _info.needTextAnnouncing())
+ {
+ if (this._state.isAnnounced())
+ {
+ this._state = DNSState.ANNOUNCING_1;
+ if (this.getDns() != null)
+ {
+ this.getDns().startAnnouncer();
+ }
+ }
+ _info.setNeedTextAnnouncing(false);
+ }
+ }
+ finally
+ {
+ this.unlock();
+ }
+ }
+ }
+
+ @Override
+ public void setDns(JmDNSImpl dns)
+ {
+ super.setDns(dns);
+ }
+
+ }
/**
- * Task associated to this service info.
- * Possible tasks are JmDNS.Prober, JmDNS.Announcer, JmDNS.Responder,
- * JmDNS.Canceler.
- */
- private TimerTask task;
-
- String type;
- private String name;
- String server;
- int port;
- int weight;
- int priority;
- private byte text[];
- Hashtable props;
- InetAddress addr;
+ * @param type
+ * @param name
+ * @param subtype
+ * @param port
+ * @param weight
+ * @param priority
+ * @param persistent
+ * @param text
+ * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, String)
+ */
+ public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, String text)
+ {
+ this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, persistent, (byte[]) null);
+ _server = text;
+ try
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream(text.length());
+ writeUTF(out, text);
+ this._text = out.toByteArray();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("unexpected exception: " + e);
+ }
+ }
/**
- * @see javax.jmdns.ServiceInfo#create(String, String, int, String)
+ * @param type
+ * @param name
+ * @param subtype
+ * @param port
+ * @param weight
+ * @param priority
+ * @param persistent
+ * @param props
+ * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, Map)
*/
- public ServiceInfoImpl(String type, String name, int port, String text)
+ public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, Map<String, ?> props)
{
- this(type, name, port, 0, 0, text);
+ this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, persistent, textFromProperties(props));
}
-
+
/**
- * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, String)
+ * @param type
+ * @param name
+ * @param subtype
+ * @param port
+ * @param weight
+ * @param priority
+ * @param persistent
+ * @param text
+ * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, byte[])
*/
- public ServiceInfoImpl(String type, String name, int port, int weight, int priority, String text)
+ public ServiceInfoImpl(String type, String name, String subtype, int port, int weight, int priority, boolean persistent, byte text[])
+ {
+ this(ServiceInfoImpl.decodeQualifiedNameMap(type, name, subtype), port, weight, priority, persistent, text);
+ }
+
+ public ServiceInfoImpl(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, Map<String, ?> props)
+ {
+ this(qualifiedNameMap, port, weight, priority, persistent, textFromProperties(props));
+ }
+
+ ServiceInfoImpl(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, String text)
{
- this(type, name, port, weight, priority, (byte[]) null);
+ this(qualifiedNameMap, port, weight, priority, persistent, (byte[]) null);
+ _server = text;
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream(text.length());
writeUTF(out, text);
- byte [] data = out.toByteArray();
- this.setText(new byte[data.length + 1]);
- this.getText()[0] = (byte) data.length;
- System.arraycopy(data, 0, this.getText(), 1, data.length);
+ this._text = out.toByteArray();
}
catch (IOException e)
{
@@ -86,210 +192,418 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
}
}
+ ServiceInfoImpl(Map<Fields, String> qualifiedNameMap, int port, int weight, int priority, boolean persistent, byte text[])
+ {
+ Map<Fields, String> map = ServiceInfoImpl.checkQualifiedNameMap(qualifiedNameMap);
+
+ this._domain = map.get(Fields.Domain);
+ this._protocol = map.get(Fields.Protocol);
+ this._application = map.get(Fields.Application);
+ this._name = map.get(Fields.Instance);
+ this._subtype = map.get(Fields.Subtype);
+
+ this._port = port;
+ this._weight = weight;
+ this._priority = priority;
+ this._text = text;
+ this.setNeedTextAnnouncing(false);
+ this._state = new ServiceInfoState(this);
+ this._persistent = persistent;
+ }
+
/**
- * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, Hashtable)
+ * During recovery we need to duplicate service info to reregister them
+ *
+ * @param info
*/
- public ServiceInfoImpl(String type, String name, int port, int weight, int priority, Hashtable props)
+ public ServiceInfoImpl(ServiceInfo info)
{
- this(type, name, port, weight, priority, new byte[0]);
- if (props != null)
+ if (info != null)
+ {
+ this._domain = info.getDomain();
+ this._protocol = info.getProtocol();
+ this._application = info.getApplication();
+ this._name = info.getName();
+ this._subtype = info.getSubtype();
+ this._port = info.getPort();
+ this._weight = info.getWeight();
+ this._priority = info.getPriority();
+ this._text = info.getTextBytes();
+ this._persistent = info.isPersistent();
+ this._ipv4Addr = info.getInet4Address();
+ this._ipv6Addr = info.getInet6Address();
+ }
+ this._state = new ServiceInfoState(this);
+ }
+
+ public static Map<Fields, String> decodeQualifiedNameMap(String type, String name, String subtype)
+ {
+ Map<Fields, String> qualifiedNameMap = decodeQualifiedNameMapForType(type);
+
+ qualifiedNameMap.put(Fields.Instance, name);
+ qualifiedNameMap.put(Fields.Subtype, subtype);
+
+ return checkQualifiedNameMap(qualifiedNameMap);
+ }
+
+ public static Map<Fields, String> decodeQualifiedNameMapForType(String type)
+ {
+ String aType = type.toLowerCase();
+ Map<Fields, String> qualifiedNameMap = new HashMap<Fields, String>(5);
+
+ String domain = "";
+ String protocol = "";
+ String application = aType;
+ String name = "";
+ String subtype = "";
+ if (aType.contains("in-addr.arpa") || aType.contains("ip6.arpa"))
+ {
+ int index = (aType.contains("in-addr.arpa") ? aType.indexOf("in-addr.arpa") : aType.indexOf("ip6.arpa"));
+ name = removeSeparators(aType.substring(0, index));
+ domain = removeSeparators(aType.substring(index));
+ protocol = "";
+ application = "";
+ }
+ else if ((!aType.contains("_")) && aType.contains("."))
+ {
+ int index = aType.indexOf('.');
+ name = removeSeparators(aType.substring(0, index));
+ domain = removeSeparators(aType.substring(index));
+ protocol = "";
+ application = "";
+ }
+ else
{
- try
{
- ByteArrayOutputStream out = new ByteArrayOutputStream(256);
- for (Enumeration e = props.keys(); e.hasMoreElements();)
+ // First remove the name if it there.
+ if (!aType.startsWith("_") || aType.startsWith("_services"))
{
- String key = (String) e.nextElement();
- Object val = props.get(key);
- ByteArrayOutputStream out2 = new ByteArrayOutputStream(100);
- writeUTF(out2, key);
- if (val instanceof String)
- {
- out2.write('=');
- writeUTF(out2, (String) val);
- }
- else
+ int index = aType.indexOf('.');
+ if (index > 0)
{
- if (val instanceof byte[])
- {
- out2.write('=');
- byte[] bval = (byte[]) val;
- out2.write(bval, 0, bval.length);
- }
- else
- {
- if (val != NO_VALUE)
- {
- throw new IllegalArgumentException("invalid property value: " + val);
- }
- }
+ // We need to preserve the case for the user readable name.
+ name = type.substring(0, index);
+ if (index + 1 < aType.length())
+ aType = aType.substring(index + 1);
}
- byte data[] = out2.toByteArray();
- out.write(data.length);
- out.write(data, 0, data.length);
}
- this.setText(out.toByteArray());
}
- catch (IOException e)
+
{
- throw new RuntimeException("unexpected exception: " + e);
+ int index = aType.lastIndexOf("._");
+ if (index > 0)
+ {
+ int start = index + 2;
+ int end = aType.indexOf('.', start);
+ protocol = aType.substring(start, end);
+ }
+ }
+ if (protocol.length() > 0)
+ {
+ int index = aType.indexOf("_" + protocol + ".");
+ {
+ int start = index + protocol.length() + 2;
+ int end = aType.length() - (aType.endsWith(".") ? 1 : 0);
+ domain = aType.substring(start, end);
+ application = aType.substring(0, index - 1);
+ }
+ }
+ {
+ int index = application.indexOf("._sub");
+ if (index > 0)
+ {
+ int start = index + 5;
+ subtype = removeSeparators(application.substring(0, index));
+ application = removeSeparators(application.substring(start));
+ }
+ application = removeSeparators(application);
}
}
- }
+ qualifiedNameMap.put(Fields.Domain, domain);
+ qualifiedNameMap.put(Fields.Protocol, protocol);
+ qualifiedNameMap.put(Fields.Application, application);
+ qualifiedNameMap.put(Fields.Instance, name);
+ qualifiedNameMap.put(Fields.Subtype, subtype);
- /**
- * @see javax.jmdns.ServiceInfo#create(String, String, int, int, int, byte[])
- */
- public ServiceInfoImpl(String type, String name, int port, int weight, int priority, byte text[])
- {
- this.type = type;
- this.name = name;
- this.port = port;
- this.weight = weight;
- this.priority = priority;
- this.setText(text);
+ return qualifiedNameMap;
}
- /**
- * Construct a service record during service discovery.
- */
- ServiceInfoImpl(String type, String name)
+ protected static Map<Fields, String> checkQualifiedNameMap(Map<Fields, String> qualifiedNameMap)
{
- if (!type.endsWith("."))
- {
- throw new IllegalArgumentException("type must be fully qualified DNS name ending in '.': " + type);
- }
+ Map<Fields, String> checkedQualifiedNameMap = new HashMap<Fields, String>(5);
+
+ // Optional domain
+ String domain = (qualifiedNameMap.containsKey(Fields.Domain) ? qualifiedNameMap.get(Fields.Domain) : "local");
+ if ((domain == null) || (domain.length() == 0))
+ domain = "local";
+ domain = removeSeparators(domain);
+ checkedQualifiedNameMap.put(Fields.Domain, domain);
+ // Optional protocol
+ String protocol = (qualifiedNameMap.containsKey(Fields.Protocol) ? qualifiedNameMap.get(Fields.Protocol) : "tcp");
+ if ((protocol == null) || (protocol.length() == 0))
+ protocol = "tcp";
+ protocol = removeSeparators(protocol);
+ checkedQualifiedNameMap.put(Fields.Protocol, protocol);
+ // Application
+ String application = (qualifiedNameMap.containsKey(Fields.Application) ? qualifiedNameMap.get(Fields.Application) : "");
+ if ((application == null) || (application.length() == 0))
+ application = "";
+ application = removeSeparators(application);
+ checkedQualifiedNameMap.put(Fields.Application, application);
+ // Instance
+ String instance = (qualifiedNameMap.containsKey(Fields.Instance) ? qualifiedNameMap.get(Fields.Instance) : "");
+ if ((instance == null) || (instance.length() == 0))
+ throw new IllegalArgumentException("The instance name component of a fully qualified service cannot be empty.");
+ instance = removeSeparators(instance);
+ checkedQualifiedNameMap.put(Fields.Instance, instance);
+ // Optional Subtype
+ String subtype = (qualifiedNameMap.containsKey(Fields.Subtype) ? qualifiedNameMap.get(Fields.Subtype) : "");
+ if ((instance == null) || (instance.length() == 0))
+ instance = "";
+ subtype = removeSeparators(subtype);
+ checkedQualifiedNameMap.put(Fields.Subtype, subtype);
- this.type = type;
- this.name = name;
+ return checkedQualifiedNameMap;
}
- /**
- * During recovery we need to duplicate service info to reregister them
- */
- ServiceInfoImpl(ServiceInfoImpl info)
+ private static String removeSeparators(String name)
{
- if (info != null)
+ String newName = name.trim();
+ if (newName.startsWith("."))
{
- this.type = info.type;
- this.name = info.name;
- this.port = info.port;
- this.weight = info.weight;
- this.priority = info.priority;
- this.setText(info.getText());
+ newName = newName.substring(1);
}
+ if (newName.startsWith("_"))
+ {
+ newName = newName.substring(1);
+ }
+ if (newName.endsWith("."))
+ {
+ newName = newName.substring(0, newName.length() - 1);
+ }
+ return newName;
}
- /**
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getType()
*/
+ @Override
public String getType()
{
- return type;
+ String domain = this.getDomain();
+ String protocol = this.getProtocol();
+ String application = this.getApplication();
+ return (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
}
- /**
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getTypeWithSubtype()
+ */
+ @Override
+ public String getTypeWithSubtype()
+ {
+ String subtype = this.getSubtype();
+ return (subtype.length() > 0 ? "_" + subtype.toLowerCase() + "._sub." : "") + this.getType();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getName()
*/
+ @Override
public String getName()
{
- return name;
+ return (_name != null ? _name : "");
}
/**
* Sets the service instance name.
*
- * @param name unqualified service instance name, such as <code>foobar</code>
+ * @param name
+ * unqualified service instance name, such as <code>foobar</code>
*/
void setName(String name)
{
- this.name = name;
+ this._name = name;
}
- /**
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getQualifiedName()
*/
+ @Override
public String getQualifiedName()
{
- return name + "." + type;
+ String domain = this.getDomain();
+ String protocol = this.getProtocol();
+ String application = this.getApplication();
+ String instance = this.getName();
+ // String subtype = this.getSubtype();
+ // return (instance.length() > 0 ? instance + "." : "") + (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + (subtype.length() > 0 ? ",_" + subtype.toLowerCase() + "." : ".") : "") + domain
+ // + ".";
+ return (instance.length() > 0 ? instance + "." : "") + (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
}
/**
* @see javax.jmdns.ServiceInfo#getServer()
*/
+ @Override
public String getServer()
{
- return server;
+ return (_server != null ? _server : "");
}
/**
+ * @param server
+ * the server to set
+ */
+ void setServer(String server)
+ {
+ this._server = server;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getHostAddress()
*/
+ @Override
public String getHostAddress()
{
- return (addr != null ? addr.getHostAddress() : "");
+ return (this.getInetAddress() != null ? this.getInetAddress().getHostAddress() : "");
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getAddress()
+ */
+ @Deprecated
+ @Override
public InetAddress getAddress()
{
- return addr;
+ return this.getInetAddress();
+ }
+
+ /**
+ * @param addr
+ * the addr to set
+ */
+ void setAddress(Inet4Address addr)
+ {
+ this._ipv4Addr = addr;
}
/**
+ * @param addr
+ * the addr to set
+ */
+ void setAddress(Inet6Address addr)
+ {
+ this._ipv6Addr = addr;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getInetAddress()
*/
+ @Override
public InetAddress getInetAddress()
{
- return addr;
+ return (_ipv4Addr != null ? _ipv4Addr : _ipv6Addr);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getInet4Address()
+ */
+ @Override
+ public Inet4Address getInet4Address()
+ {
+ return _ipv4Addr;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getInet6Address()
+ */
+ @Override
+ public Inet6Address getInet6Address()
+ {
+ return _ipv6Addr;
}
/**
* @see javax.jmdns.ServiceInfo#getPort()
*/
+ @Override
public int getPort()
{
- return port;
+ return _port;
}
/**
* @see javax.jmdns.ServiceInfo#getPriority()
*/
+ @Override
public int getPriority()
{
- return priority;
+ return _priority;
}
/**
* @see javax.jmdns.ServiceInfo#getWeight()
*/
+ @Override
public int getWeight()
{
- return weight;
+ return _weight;
}
/**
* @see javax.jmdns.ServiceInfo#getTextBytes()
*/
+ @Override
public byte[] getTextBytes()
{
return getText();
}
- /**
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getTextString()
*/
+ @Deprecated
+ @Override
public String getTextString()
{
- if ((getText() == null) || (getText().length == 0) || ((getText().length == 1) && (getText()[0] == 0)))
+ Map<String, byte[]> properties = this.getProperties();
+ for (String key : properties.keySet())
{
- return null;
+ byte[] value = properties.get(key);
+ if ((value != null) && (value.length > 0))
+ {
+ return key + "=" + new String(value);
+ }
+ return key;
}
- return readUTF(getText(), 0, getText().length);
+ return "";
}
/**
* @see javax.jmdns.ServiceInfo#getURL()
*/
+ @Override
public String getURL()
{
return getURL("http");
@@ -298,6 +612,7 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
/**
* @see javax.jmdns.ServiceInfo#getURL(java.lang.String)
*/
+ @Override
public String getURL(String protocol)
{
String url = protocol + "://" + getHostAddress() + ":" + getPort();
@@ -316,20 +631,26 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
return url;
}
- /**
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getPropertyBytes(java.lang.String)
*/
+ @Override
public synchronized byte[] getPropertyBytes(String name)
{
- return (byte[]) getProperties().get(name);
+ return this.getProperties().get(name);
}
- /**
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getPropertyString(java.lang.String)
*/
+ @Override
public synchronized String getPropertyString(String name)
{
- byte data[] = (byte[]) getProperties().get(name);
+ byte data[] = this.getProperties().get(name);
if (data == null)
{
return null;
@@ -341,19 +662,85 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
return readUTF(data, 0, data.length);
}
- /**
+ /*
+ * (non-Javadoc)
+ *
* @see javax.jmdns.ServiceInfo#getPropertyNames()
*/
- public Enumeration getPropertyNames()
+ @Override
+ public Enumeration<String> getPropertyNames()
{
- Hashtable props = getProperties();
- return (props != null) ? props.keys() : new Vector().elements();
+ Map<String, byte[]> properties = getProperties();
+ Collection<String> names = (properties != null ? properties.keySet() : Collections.<String> emptySet());
+ return new Vector<String>(names).elements();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getApplication()
+ */
+ @Override
+ public String getApplication()
+ {
+ return (_application != null ? _application : "");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getDomain()
+ */
+ @Override
+ public String getDomain()
+ {
+ return (_domain != null ? _domain : "local");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getProtocol()
+ */
+ @Override
+ public String getProtocol()
+ {
+ return (_protocol != null ? _protocol : "tcp");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getSubtype()
+ */
+ @Override
+ public String getSubtype()
+ {
+ return (_subtype != null ? _subtype : "");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getQualifiedNameMap()
+ */
+ @Override
+ public Map<Fields, String> getQualifiedNameMap()
+ {
+ Map<Fields, String> map = new HashMap<Fields, String>(5);
+
+ map.put(Fields.Domain, this.getDomain());
+ map.put(Fields.Protocol, this.getProtocol());
+ map.put(Fields.Application, this.getApplication());
+ map.put(Fields.Instance, this.getName());
+ map.put(Fields.Subtype, this.getSubtype());
+ return map;
}
/**
* Write a UTF string with a length to a stream.
*/
- void writeUTF(OutputStream out, String str) throws IOException
+ static void writeUTF(OutputStream out, String str) throws IOException
{
for (int i = 0, len = str.length(); i < len; i++)
{
@@ -384,10 +771,11 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
*/
String readUTF(byte data[], int off, int len)
{
+ int offset = off;
StringBuffer buf = new StringBuffer();
- for (int end = off + len; off < end;)
+ for (int end = offset + len; offset < end;)
{
- int ch = data[off++] & 0xFF;
+ int ch = data[offset++] & 0xFF;
switch (ch >> 4)
{
case 0:
@@ -402,28 +790,28 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
break;
case 12:
case 13:
- if (off >= len)
+ if (offset >= len)
{
return null;
}
- // 110x xxxx 10xx xxxx
- ch = ((ch & 0x1F) << 6) | (data[off++] & 0x3F);
+ // 110x xxxx 10xx xxxx
+ ch = ((ch & 0x1F) << 6) | (data[offset++] & 0x3F);
break;
case 14:
- if (off + 2 >= len)
+ if (offset + 2 >= len)
{
return null;
}
- // 1110 xxxx 10xx xxxx 10xx xxxx
- ch = ((ch & 0x0f) << 12) | ((data[off++] & 0x3F) << 6) | (data[off++] & 0x3F);
+ // 1110 xxxx 10xx xxxx 10xx xxxx
+ ch = ((ch & 0x0f) << 12) | ((data[offset++] & 0x3F) << 6) | (data[offset++] & 0x3F);
break;
default:
- if (off + 1 >= len)
+ if (offset + 1 >= len)
{
return null;
}
- // 10xx xxxx, 1111 xxxx
- ch = ((ch & 0x3F) << 4) | (data[off++] & 0x0f);
+ // 10xx xxxx, 1111 xxxx
+ ch = ((ch & 0x3F) << 4) | (data[offset++] & 0x0f);
break;
}
buf.append((char) ch);
@@ -431,11 +819,11 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
return buf.toString();
}
- synchronized Hashtable getProperties()
+ synchronized Map<String, byte[]> getProperties()
{
- if ((props == null) && (getText() != null))
+ if ((_props == null) && (getText() != null))
{
- Hashtable props = new Hashtable();
+ Hashtable<String, byte[]> properties = new Hashtable<String, byte[]>();
int off = 0;
while (off < getText().length)
{
@@ -443,156 +831,330 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
int len = getText()[off++] & 0xFF;
if ((len == 0) || (off + len > getText().length))
{
- props.clear();
+ properties.clear();
break;
}
// look for the '='
int i = 0;
for (; (i < len) && (getText()[off + i] != '='); i++)
{
- ;
+ /* Stub */
}
// get the property name
String name = readUTF(getText(), off, i);
if (name == null)
{
- props.clear();
+ properties.clear();
break;
}
if (i == len)
{
- props.put(name, NO_VALUE);
+ properties.put(name, NO_VALUE);
}
else
{
byte value[] = new byte[len - ++i];
System.arraycopy(getText(), off + i, value, 0, len - i);
- props.put(name, value);
+ properties.put(name, value);
off += len;
}
}
- this.props = props;
+ this._props = properties;
}
- return props;
+ return (_props != null ? _props : Collections.<String, byte[]> emptyMap());
}
-
+
/**
* JmDNS callback to update a DNS record.
+ *
+ * @param dnsCache
+ * @param now
+ * @param rec
*/
- public void updateRecord(JmDNSImpl jmdns, long now, DNSRecord rec)
+ @Override
+ public void updateRecord(DNSCache dnsCache, long now, DNSEntry rec)
{
- if ((rec != null) && !rec.isExpired(now))
+ if ((rec instanceof DNSRecord) && !rec.isExpired(now))
{
- switch (rec.type)
+ boolean serviceUpdated = false;
+ switch (rec.getRecordType())
{
- case DNSConstants.TYPE_A: // IPv4
- case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested
- if (rec.name.equals(server))
+ case TYPE_A: // IPv4
+ if (rec.getName().equalsIgnoreCase(this.getServer()))
{
- addr = ((DNSRecord.Address) rec).getAddress();
-
+ _ipv4Addr = (Inet4Address) ((DNSRecord.Address) rec).getAddress();
+ serviceUpdated = true;
+ }
+ break;
+ case TYPE_AAAA: // IPv6
+ if (rec.getName().equalsIgnoreCase(this.getServer()))
+ {
+ _ipv6Addr = (Inet6Address) ((DNSRecord.Address) rec).getAddress();
+ serviceUpdated = true;
}
break;
- case DNSConstants.TYPE_SRV:
- if (rec.name.equals(getQualifiedName()))
+ case TYPE_SRV:
+ if (rec.getName().equalsIgnoreCase(this.getQualifiedName()))
{
DNSRecord.Service srv = (DNSRecord.Service) rec;
- server = srv.server;
- port = srv.port;
- weight = srv.weight;
- priority = srv.priority;
- addr = null;
- // changed to use getCache() instead - jeffs
- // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN));
- updateRecord(jmdns, now, (DNSRecord) jmdns.getCache().get(server, DNSConstants.TYPE_A, DNSConstants.CLASS_IN));
+ boolean serverChanged = (_server == null) || !_server.equalsIgnoreCase(srv.getServer());
+ _server = srv.getServer();
+ _port = srv.getPort();
+ _weight = srv.getWeight();
+ _priority = srv.getPriority();
+ if (serverChanged)
+ {
+ _ipv4Addr = null;
+ _ipv6Addr = null;
+ this.updateRecord(dnsCache, now, dnsCache.getDNSEntry(_server, DNSRecordType.TYPE_A, DNSRecordClass.CLASS_IN));
+ this.updateRecord(dnsCache, now, dnsCache.getDNSEntry(_server, DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_IN));
+ // We do not want to trigger the listener in this case as it will be triggered if the address resolves.
+ }
+ else
+ {
+ serviceUpdated = true;
+ }
}
break;
- case DNSConstants.TYPE_TXT:
- if (rec.name.equals(getQualifiedName()))
+ case TYPE_TXT:
+ if (rec.getName().equalsIgnoreCase(this.getQualifiedName()))
{
DNSRecord.Text txt = (DNSRecord.Text) rec;
- setText(txt.text);
+ _text = txt._text;
+ serviceUpdated = true;
+ }
+ break;
+ case TYPE_PTR:
+ if ((this.getSubtype().length() == 0) && (rec.getSubtype().length() != 0))
+ {
+ _subtype = rec.getSubtype();
+ serviceUpdated = true;
}
break;
+ default:
+ break;
}
- // Future Design Pattern
- // This is done, to notify the wait loop in method
- // JmDNS.getServiceInfo(type, name, timeout);
- if (hasData() && getDns() != null)
+ if (serviceUpdated && this.hasData())
{
- getDns().handleServiceResolved(this);
- setDns(null);
+ JmDNSImpl dns = this.getDns();
+ if (dns != null)
+ {
+ ServiceEvent event = ((DNSRecord) rec).getServiceEvent(dns);
+ event = new ServiceEventImpl(dns, event.getType(), event.getName(), this);
+ dns.handleServiceResolved(event);
+ }
}
+ // This is done, to notify the wait loop in method JmDNS.waitForInfoData(ServiceInfo info, int timeout);
synchronized (this)
{
- notifyAll();
+ this.notifyAll();
}
}
}
/**
* Returns true if the service info is filled with data.
+ *
+ * @return <code>true</code> if the service info has data, <code>false</code> otherwise.
*/
- public boolean hasData()
+ @Override
+ public synchronized boolean hasData()
{
- return server != null && addr != null && getText() != null;
+ return this.getServer() != null && this.getInetAddress() != null && this.getTextBytes() != null && this.getTextBytes().length > 0;
+ // return this.getServer() != null && (this.getAddress() != null || (this.getTextBytes() != null && this.getTextBytes().length > 0));
}
-
-
+
// State machine
- /**
- * Sets the state and notifies all objects that wait on the ServiceInfo.
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#advanceState(javax.jmdns.impl.tasks.DNSTask)
*/
- public synchronized void advanceState()
+ @Override
+ public boolean advanceState(DNSTask task)
{
- state = state.advance();
- notifyAll();
+ return _state.advanceState(task);
}
- /**
- * Sets the state and notifies all objects that wait on the ServiceInfo.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#revertState()
*/
- synchronized void revertState()
+ @Override
+ public boolean revertState()
{
- state = state.revert();
- notifyAll();
+ return _state.revertState();
}
- /**
- * Sets the state and notifies all objects that wait on the ServiceInfo.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#cancel()
*/
- synchronized void cancel()
+ @Override
+ public boolean cancelState()
{
- state = DNSState.CANCELED;
- notifyAll();
+ return _state.cancelState();
}
- /**
- * Returns the current state of this info.
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#recover()
*/
- public DNSState getState()
+ @Override
+ public boolean recoverState()
{
- return state;
+ return this._state.recoverState();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#removeAssociationWithTask(javax.jmdns.impl.tasks.DNSTask)
+ */
+ @Override
+ public void removeAssociationWithTask(DNSTask task)
+ {
+ _state.removeAssociationWithTask(task);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#associateWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public void associateWithTask(DNSTask task, DNSState state)
+ {
+ _state.associateWithTask(task, state);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAssociatedWithTask(javax.jmdns.impl.tasks.DNSTask, javax.jmdns.impl.constants.DNSState)
+ */
+ @Override
+ public boolean isAssociatedWithTask(DNSTask task, DNSState state)
+ {
+ return _state.isAssociatedWithTask(task, state);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isProbing()
+ */
+ @Override
+ public boolean isProbing()
+ {
+ return _state.isProbing();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAnnouncing()
+ */
+ @Override
+ public boolean isAnnouncing()
+ {
+ return _state.isAnnouncing();
+ }
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isAnnounced()
+ */
+ @Override
+ public boolean isAnnounced()
+ {
+ return _state.isAnnounced();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceling()
+ */
+ @Override
+ public boolean isCanceling()
+ {
+ return this._state.isCanceling();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#isCanceled()
+ */
+ @Override
+ public boolean isCanceled()
+ {
+ return _state.isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForAnnounced(long)
+ */
+ @Override
+ public boolean waitForAnnounced(long timeout)
+ {
+ return _state.waitForAnnounced(timeout);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.DNSStatefulObject#waitForCanceled(long)
+ */
+ @Override
+ public boolean waitForCanceled(long timeout)
+ {
+ return _state.waitForCanceled(timeout);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
public int hashCode()
{
return getQualifiedName().hashCode();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
public boolean equals(Object obj)
{
return (obj instanceof ServiceInfoImpl) && getQualifiedName().equals(((ServiceInfoImpl) obj).getQualifiedName());
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#getNiceTextString()
+ */
+ @Override
public String getNiceTextString()
{
StringBuffer buf = new StringBuffer();
- for (int i = 0, len = getText().length; i < len; i++)
+ for (int i = 0, len = this.getText().length; i < len; i++)
{
- if (i >= 20)
+ if (i >= 200)
{
buf.append("...");
break;
@@ -611,73 +1173,212 @@ public class ServiceInfoImpl extends ServiceInfo implements DNSListener
return buf.toString();
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#clone()
+ */
+ @Override
+ public Object clone()
+ {
+ return new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, _persistent, _text);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
public String toString()
{
- StringBuffer buf = new StringBuffer();
- buf.append("service[");
- buf.append(getQualifiedName());
- buf.append(',');
- buf.append(getAddress());
+ StringBuilder buf = new StringBuilder();
+ buf.append("[" + this.getClass().getSimpleName() + "@" + System.identityHashCode(this) + " ");
+ buf.append("name: '");
+ buf.append((this.getName().length() > 0 ? this.getName() + "." : "") + this.getTypeWithSubtype());
+ buf.append("' address: '");
+ buf.append(this.getInetAddress());
buf.append(':');
- buf.append(port);
- buf.append(',');
- buf.append(getNiceTextString());
+ buf.append(this.getPort());
+ buf.append("' status: '");
+ buf.append(_state.toString());
+ buf.append(this.isPersistent() ? "' is persistent," : "',");
+ buf.append(" has ");
+ buf.append(this.hasData() ? "" : "NO ");
+ buf.append("data");
+ if (this.getText().length > 0)
+ {
+ // buf.append("\n");
+ // buf.append(this.getNiceTextString());
+ Map<String, byte[]> properties = this.getProperties();
+ if (!properties.isEmpty())
+ {
+ buf.append("\n");
+ for (String key : properties.keySet())
+ {
+ buf.append("\t" + key + ": " + new String(properties.get(key)) + "\n");
+ }
+ }
+ else
+ {
+ buf.append(" empty");
+ }
+ }
buf.append(']');
return buf.toString();
}
- public void addAnswers(DNSOutgoing out, int ttl, HostInfo localHost) throws IOException
+ public Collection<DNSRecord> answers(boolean unique, int ttl, HostInfo localHost)
{
- out.addAnswer(new Pointer(type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, ttl,
- getQualifiedName()), 0);
- out.addAnswer(new Service(getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN|DNSConstants.CLASS_UNIQUE,
- ttl, priority, weight, port, localHost.getName()), 0);
- out.addAnswer(new Text(getQualifiedName(), DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN|DNSConstants.CLASS_UNIQUE,
- ttl, getText()), 0);
+ List<DNSRecord> list = new ArrayList<DNSRecord>();
+ if (this.getSubtype().length() > 0)
+ {
+ list.add(new Pointer(this.getTypeWithSubtype(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, ttl, this.getQualifiedName()));
+ }
+ list.add(new Pointer(this.getType(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, ttl, this.getQualifiedName()));
+ list.add(new Service(this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, _priority, _weight, _port, localHost.getName()));
+ list.add(new Text(this.getQualifiedName(), DNSRecordClass.CLASS_IN, unique, ttl, this.getText()));
+ return list;
}
- public void setTask(TimerTask task)
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#setText(byte[])
+ */
+ @Override
+ public void setText(byte[] text) throws IllegalStateException
{
- this.task = task;
+ synchronized (this)
+ {
+ this._text = text;
+ this.setNeedTextAnnouncing(true);
+ }
}
- public TimerTask getTask()
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#setText(java.util.Map)
+ */
+ @Override
+ public void setText(Map<String, ?> props) throws IllegalStateException
{
- return task;
+ this.setText(textFromProperties(props));
}
- public void setText(byte [] text)
+ /**
+ * This is used internally by the framework
+ *
+ * @param text
+ */
+ void _setText(byte[] text)
{
- this.text = text;
+ this._text = text;
}
- public byte [] getText()
+ private static byte[] textFromProperties(Map<String, ?> props)
{
- return text;
+ if (props != null)
+ {
+ try
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream(256);
+ for (String key : props.keySet())
+ {
+ Object val = props.get(key);
+ ByteArrayOutputStream out2 = new ByteArrayOutputStream(100);
+ writeUTF(out2, key);
+ if (val == null)
+ {
+ // Skip
+ }
+ else if (val instanceof String)
+ {
+ out2.write('=');
+ writeUTF(out2, (String) val);
+ }
+ else if (val instanceof byte[])
+ {
+ byte[] bval = (byte[]) val;
+ if (bval.length > 0)
+ {
+ out2.write('=');
+ out2.write(bval, 0, bval.length);
+ }
+ else
+ {
+ val = null;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException("invalid property value: " + val);
+ }
+ byte data[] = out2.toByteArray();
+ if (data.length > 255)
+ {
+ throw new IOException("Cannot have individual values larger that 255 chars. Offending value: " + key + (val != null ? "" : "=" + val));
+ }
+ out.write((byte) data.length);
+ out.write(data, 0, data.length);
+ }
+ return out.toByteArray();
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException("unexpected exception: " + e);
+ }
+ }
+ return new byte[0];
+ }
+
+ public byte[] getText()
+ {
+ return (this._text != null ? this._text : new byte[] {});
}
public void setDns(JmDNSImpl dns)
{
- this.dns = dns;
+ this._state.setDns(dns);
}
+ @Override
public JmDNSImpl getDns()
{
- return dns;
+ return this._state.getDns();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.ServiceInfo#isPersistent()
+ */
+ @Override
+ public boolean isPersistent()
+ {
+ return _persistent;
}
- public String getDomain()
- {
- String protocol = getProtocol();
- int start = type.indexOf(protocol) + protocol.length() + 1;
- int end = type.length() - 1;
- return type.substring(start, end);
- }
+ /**
+ * @param needTextAnnouncing
+ * the needTextAnnouncing to set
+ */
+ public void setNeedTextAnnouncing(boolean needTextAnnouncing)
+ {
+ this._needTextAnnouncing = needTextAnnouncing;
+ if (this._needTextAnnouncing)
+ {
+ _state.setTask(null);
+ }
+ }
+
+ /**
+ * @return the needTextAnnouncing
+ */
+ public boolean needTextAnnouncing()
+ {
+ return _needTextAnnouncing;
+ }
- public String getProtocol()
- {
- int start = type.lastIndexOf("._") + 2;
- int end = type.indexOf('.', start);
- return type.substring(start, end);
- }
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/SocketListener.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/SocketListener.java
index c36d6dcdb..3b79cdba4 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/SocketListener.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/SocketListener.java
@@ -6,82 +6,106 @@ package javax.jmdns.impl;
import java.io.IOException;
import java.net.DatagramPacket;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.constants.DNSConstants;
/**
* Listen for multicast packets.
*/
class SocketListener implements Runnable
{
-// static Logger logger = Logger.getLogger(SocketListener.class.getName());
+ static Logger logger = Logger.getLogger(SocketListener.class.getName());
/**
- *
+ *
*/
- private final JmDNSImpl jmDNSImpl;
+ private final JmDNSImpl _jmDNSImpl;
/**
* @param jmDNSImpl
*/
SocketListener(JmDNSImpl jmDNSImpl)
{
- this.jmDNSImpl = jmDNSImpl;
+ super();
+ this._jmDNSImpl = jmDNSImpl;
}
+ @Override
public void run()
{
try
{
byte buf[] = new byte[DNSConstants.MAX_MSG_ABSOLUTE];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
- while (this.jmDNSImpl.getState() != DNSState.CANCELED)
+ while (!this._jmDNSImpl.isCanceling() && !this._jmDNSImpl.isCanceled())
{
packet.setLength(buf.length);
- this.jmDNSImpl.getSocket().receive(packet);
- if (this.jmDNSImpl.getState() == DNSState.CANCELED)
+ this._jmDNSImpl.getSocket().receive(packet);
+ if (this._jmDNSImpl.isCanceling() || this._jmDNSImpl.isCanceled())
{
break;
}
try
{
- if (this.jmDNSImpl.getLocalHost().shouldIgnorePacket(packet))
+ if (this._jmDNSImpl.getLocalHost().shouldIgnorePacket(packet))
{
continue;
}
DNSIncoming msg = new DNSIncoming(packet);
-// logger.finest("SocketListener.run() JmDNS in:" + msg.print(true));
-
- synchronized (this.jmDNSImpl.getIoLock())
+ if (logger.isLoggable(Level.FINEST))
{
- if (msg.isQuery())
- {
- if (packet.getPort() != DNSConstants.MDNS_PORT)
- {
- this.jmDNSImpl.handleQuery(msg, packet.getAddress(), packet.getPort());
- }
- this.jmDNSImpl.handleQuery(msg, this.jmDNSImpl.getGroup(), DNSConstants.MDNS_PORT);
- }
- else
+ logger.finest(this.getName() + ".run() JmDNS in:" + msg.print(true));
+ }
+ if (msg.isQuery())
+ {
+ if (packet.getPort() != DNSConstants.MDNS_PORT)
{
- this.jmDNSImpl.handleResponse(msg);
+ this._jmDNSImpl.handleQuery(msg, packet.getAddress(), packet.getPort());
}
+ this._jmDNSImpl.handleQuery(msg, this._jmDNSImpl.getGroup(), DNSConstants.MDNS_PORT);
+ }
+ else
+ {
+ this._jmDNSImpl.handleResponse(msg);
}
}
catch (IOException e)
{
-// logger.log(Level.WARNING, "run() exception ", e);
+ logger.log(Level.WARNING, this.getName() + ".run() exception ", e);
}
}
}
catch (IOException e)
{
- if (this.jmDNSImpl.getState() != DNSState.CANCELED)
+ if (!this._jmDNSImpl.isCanceling() && !this._jmDNSImpl.isCanceled())
{
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
+ logger.log(Level.WARNING, this.getName() + ".run() exception ", e);
+ this._jmDNSImpl.recover();
}
}
+ // jP: 20010-01-18. Per issue #2933183. If this thread was stopped
+ // by closeMulticastSocket, we need to signal the other party via
+ // the jmDNS monitor. The other guy will then check to see if this
+ // thread has died.
+ // Note: This is placed here to avoid locking the IoLock object and
+ // 'this' instance together.
+ synchronized (this._jmDNSImpl)
+ {
+ this._jmDNSImpl.notifyAll();
+ }
+ }
+
+ public String getName()
+ {
+ return "SocketListener(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
}
+
+ public JmDNSImpl getDns()
+ {
+ return _jmDNSImpl;
+ }
+
} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSConstants.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSConstants.java
new file mode 100644
index 000000000..93d5ffe2c
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSConstants.java
@@ -0,0 +1,59 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.constants;
+
+/**
+ * DNS constants.
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
+ */
+public final class DNSConstants
+{
+ // http://www.iana.org/assignments/dns-parameters
+
+ // changed to final class - jeffs
+ public final static String MDNS_GROUP = "224.0.0.251";
+ public final static String MDNS_GROUP_IPV6 = "FF02::FB";
+ public final static int MDNS_PORT = Integer.parseInt(System.getProperty("net.mdns.port", "5353"));
+ public final static int DNS_PORT = 53;
+ public final static int DNS_TTL = 60 * 60; // default one hour TTL
+ // public final static int DNS_TTL = 120 * 60; // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13)
+
+ public final static int MAX_MSG_TYPICAL = 1460;
+ public final static int MAX_MSG_ABSOLUTE = 8972;
+
+ public final static int FLAGS_QR_MASK = 0x8000; // Query response mask
+ public final static int FLAGS_QR_QUERY = 0x0000; // Query
+ public final static int FLAGS_QR_RESPONSE = 0x8000; // Response
+
+ public final static int FLAGS_AA = 0x0400; // Authorative answer
+ public final static int FLAGS_TC = 0x0200; // Truncated
+ public final static int FLAGS_RD = 0x0100; // Recursion desired
+ public final static int FLAGS_RA = 0x8000; // Recursion available
+
+ public final static int FLAGS_Z = 0x0040; // Zero
+ public final static int FLAGS_AD = 0x0020; // Authentic data
+ public final static int FLAGS_CD = 0x0010; // Checking disabled
+
+ // Time Intervals for various functions
+
+ public final static int SHARED_QUERY_TIME = 20; // milliseconds before send shared query
+ public final static int QUERY_WAIT_INTERVAL = 225; // milliseconds between query loops.
+ public final static int PROBE_WAIT_INTERVAL = 250; // milliseconds between probe loops.
+ public final static int RESPONSE_MIN_WAIT_INTERVAL = 20; // minimal wait interval for response.
+ public final static int RESPONSE_MAX_WAIT_INTERVAL = 115; // maximal wait interval for response
+ public final static int PROBE_CONFLICT_INTERVAL = 1000; // milliseconds to wait after conflict.
+ public final static int PROBE_THROTTLE_COUNT = 10; // After x tries go 1 time a sec. on probes.
+ public final static int PROBE_THROTTLE_COUNT_INTERVAL = 5000; // We only increment the throttle count, if the previous increment is inside this interval.
+ public final static int ANNOUNCE_WAIT_INTERVAL = 1000; // milliseconds between Announce loops.
+ public final static int RECORD_REAPER_INTERVAL = 10000; // milliseconds between cache cleanups.
+ public final static int KNOWN_ANSWER_TTL = 120;
+ public final static int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; // 50% of the TTL in milliseconds
+
+ public final static long CLOSE_TIMEOUT = ANNOUNCE_WAIT_INTERVAL * 5L;
+ public final static long SERVICE_INFO_TIMEOUT = ANNOUNCE_WAIT_INTERVAL * 6L;
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSLabel.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSLabel.java
new file mode 100644
index 000000000..59a8cf78d
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSLabel.java
@@ -0,0 +1,97 @@
+/**
+ *
+ */
+package javax.jmdns.impl.constants;
+
+/**
+ * DNS label.
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
+ */
+public enum DNSLabel
+{
+ /**
+ * This is unallocated.
+ */
+ Unknown("", 0x80),
+ /**
+ * Standard label [RFC 1035]
+ */
+ Standard("standard label", 0x00),
+ /**
+ * Compressed label [RFC 1035]
+ */
+ Compressed("compressed label", 0xC0),
+ /**
+ * Extended label [RFC 2671]
+ */
+ Extended("extended label", 0x40);
+
+ /**
+ * DNS label types are encoded on the first 2 bits
+ */
+ final static int LABEL_MASK = 0xC0;
+ final static int LABEL_NOT_MASK = 0x3F;
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSLabel(String name, int index)
+ {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName()
+ {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue()
+ {
+ return _index;
+ }
+
+ /**
+ * @param index
+ * @return label
+ */
+ public static DNSLabel labelForByte(int index)
+ {
+ int maskedIndex = index & LABEL_MASK;
+ for (DNSLabel aLabel : DNSLabel.values())
+ {
+ if (aLabel._index == maskedIndex)
+ return aLabel;
+ }
+ return Unknown;
+ }
+
+ /**
+ * @param index
+ * @return masked value
+ */
+ public static int labelValue(int index)
+ {
+ return index & LABEL_NOT_MASK;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.name() + " index " + this.indexValue();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOperationCode.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOperationCode.java
new file mode 100644
index 000000000..73b57d39e
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOperationCode.java
@@ -0,0 +1,95 @@
+/**
+ *
+ */
+package javax.jmdns.impl.constants;
+
+/**
+ * DNS operation code.
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
+ */
+public enum DNSOperationCode
+{
+ /**
+ * Query [RFC1035]
+ */
+ Query("Query", 0),
+ /**
+ * IQuery (Inverse Query, Obsolete) [RFC3425]
+ */
+ IQuery("Inverse Query", 1),
+ /**
+ * Status [RFC1035]
+ */
+ Status("Status", 2),
+ /**
+ * Unassigned
+ */
+ Unassigned("Unassigned", 3),
+ /**
+ * Notify [RFC1996]
+ */
+ Notify("Notify", 4),
+ /**
+ * Update [RFC2136]
+ */
+ Update("Update", 5);
+
+ /**
+ * DNS RCode types are encoded on the last 4 bits
+ */
+ final static int OpCode_MASK = 0x7800;
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSOperationCode(String name, int index)
+ {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName()
+ {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue()
+ {
+ return _index;
+ }
+
+ /**
+ * @param flags
+ * @return label
+ */
+ public static DNSOperationCode operationCodeForFlags(int flags)
+ {
+ int maskedIndex = (flags & OpCode_MASK) >> 11;
+ for (DNSOperationCode aCode : DNSOperationCode.values())
+ {
+ if (aCode._index == maskedIndex)
+ return aCode;
+ }
+ return Unassigned;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.name() + " index " + this.indexValue();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOptionCode.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOptionCode.java
new file mode 100644
index 000000000..6af44392f
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSOptionCode.java
@@ -0,0 +1,87 @@
+/**
+ *
+ */
+package javax.jmdns.impl.constants;
+
+/**
+ * DNS option code.
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Pierre Frisch, Rick Blair
+ */
+public enum DNSOptionCode
+{
+
+ /**
+ * Token
+ */
+ Unknown("Unknown", 65535),
+ /**
+ * Long-Lived Queries Option [http://files.dns-sd.org/draft-sekar-dns-llq.txt]
+ */
+ LLQ("LLQ", 1),
+ /**
+ * Update Leases Option [http://files.dns-sd.org/draft-sekar-dns-ul.txt]
+ */
+ UL("UL", 2),
+ /**
+ * Name Server Identifier Option [RFC5001]
+ */
+ NSID("NSID", 3),
+ /**
+ * Owner Option [draft-cheshire-edns0-owner-option]
+ */
+ Owner("Owner", 4);
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSOptionCode(String name, int index)
+ {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName()
+ {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue()
+ {
+ return _index;
+ }
+
+ /**
+ * @param optioncode
+ * @return label
+ */
+ public static DNSOptionCode resultCodeForFlags(int optioncode)
+ {
+ int maskedIndex = optioncode;
+ for (DNSOptionCode aCode : DNSOptionCode.values())
+ {
+ if (aCode._index == maskedIndex)
+ return aCode;
+ }
+ return Unknown;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.name() + " index " + this.indexValue();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordClass.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordClass.java
new file mode 100644
index 000000000..2328221f7
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordClass.java
@@ -0,0 +1,153 @@
+/**
+ *
+ */
+package javax.jmdns.impl.constants;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * DNS Record Class
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
+ */
+public enum DNSRecordClass
+{
+ /**
+ *
+ */
+ CLASS_UNKNOWN("?", 0),
+ /**
+ * Final Static Internet
+ */
+ CLASS_IN("in", 1),
+ /**
+ * CSNET
+ */
+ CLASS_CS("cs", 2),
+ /**
+ * CHAOS
+ */
+ CLASS_CH("ch", 3),
+ /**
+ * Hesiod
+ */
+ CLASS_HS("hs", 4),
+ /**
+ * Used in DNS UPDATE [RFC 2136]
+ */
+ CLASS_NONE("none", 254),
+ /**
+ * Not a DNS class, but a DNS query class, meaning "all classes"
+ */
+ CLASS_ANY("any", 255);
+
+ private static Logger logger = Logger.getLogger(DNSRecordClass.class.getName());
+
+ /**
+ * Multicast DNS uses the bottom 15 bits to identify the record class...<br/>
+ * Except for pseudo records like OPT.
+ */
+ public final static int CLASS_MASK = 0x7FFF;
+
+ /**
+ * For answers the top bit indicates that all other cached records are now invalid.<br/>
+ * For questions it indicates that we should send a unicast response.
+ */
+ public final static int CLASS_UNIQUE = 0x8000;
+
+ /**
+ *
+ */
+ public final static boolean UNIQUE = true;
+
+ /**
+ *
+ */
+ public final static boolean NOT_UNIQUE = false;
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSRecordClass(String name, int index)
+ {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName()
+ {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue()
+ {
+ return _index;
+ }
+
+ /**
+ * Checks if the class is unique
+ *
+ * @param index
+ *
+ * @return <code>true</code> is the class is unique, <code>false</code> otherwise.
+ */
+ public boolean isUnique(int index)
+ {
+ return (this != CLASS_UNKNOWN) && ((index & CLASS_UNIQUE) != 0);
+ }
+
+ /**
+ * @param name
+ * @return class for name
+ */
+ public static DNSRecordClass classForName(String name)
+ {
+ if (name != null)
+ {
+ String aName = name.toLowerCase();
+ for (DNSRecordClass aClass : DNSRecordClass.values())
+ {
+ if (aClass._externalName.equals(aName))
+ return aClass;
+ }
+ }
+ logger.log(Level.WARNING, "Could not find record class for name: " + name);
+ return CLASS_UNKNOWN;
+ }
+
+ /**
+ * @param index
+ * @return class for name
+ */
+ public static DNSRecordClass classForIndex(int index)
+ {
+ int maskedIndex = index & CLASS_MASK;
+ for (DNSRecordClass aClass : DNSRecordClass.values())
+ {
+ if (aClass._index == maskedIndex)
+ return aClass;
+ }
+ logger.log(Level.WARNING, "Could not find record class for index: " + index);
+ return CLASS_UNKNOWN;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.name() + " index " + this.indexValue();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordType.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordType.java
new file mode 100644
index 000000000..1e7904d43
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSRecordType.java
@@ -0,0 +1,325 @@
+/**
+ *
+ */
+package javax.jmdns.impl.constants;
+
+import java.util.logging.Logger;
+
+/**
+ * DNS Record Type
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
+ */
+public enum DNSRecordType
+{
+ /**
+ * Address
+ */
+ TYPE_IGNORE("ignore", 0),
+ /**
+ * Address
+ */
+ TYPE_A("a", 1),
+ /**
+ * Name Server
+ */
+ TYPE_NS("ns", 2),
+ /**
+ * Mail Destination
+ */
+ TYPE_MD("md", 3),
+ /**
+ * Mail Forwarder
+ */
+ TYPE_MF("mf", 4),
+ /**
+ * Canonical Name
+ */
+ TYPE_CNAME("cname", 5),
+ /**
+ * Start of Authority
+ */
+ TYPE_SOA("soa", 6),
+ /**
+ * Mailbox
+ */
+ TYPE_MB("mb", 7),
+ /**
+ * Mail Group
+ */
+ TYPE_MG("mg", 8),
+ /**
+ * Mail Rename
+ */
+ TYPE_MR("mr", 9),
+ /**
+ * NULL RR
+ */
+ TYPE_NULL("null", 10),
+ /**
+ * Well-known-service
+ */
+ TYPE_WKS("wks", 11),
+ /**
+ * Domain Name pointer
+ */
+ TYPE_PTR("ptr", 12),
+ /**
+ * Host information
+ */
+ TYPE_HINFO("hinfo", 13),
+ /**
+ * Mailbox information
+ */
+ TYPE_MINFO("minfo", 14),
+ /**
+ * Mail exchanger
+ */
+ TYPE_MX("mx", 15),
+ /**
+ * Arbitrary text string
+ */
+ TYPE_TXT("txt", 16),
+ /**
+ * for Responsible Person [RFC1183]
+ */
+ TYPE_RP("rp", 17),
+ /**
+ * for AFS Data Base location [RFC1183]
+ */
+ TYPE_AFSDB("afsdb", 18),
+ /**
+ * for X.25 PSDN address [RFC1183]
+ */
+ TYPE_X25("x25", 19),
+ /**
+ * for ISDN address [RFC1183]
+ */
+ TYPE_ISDN("isdn", 20),
+ /**
+ * for Route Through [RFC1183]
+ */
+ TYPE_RT("rt", 21),
+ /**
+ * for NSAP address, NSAP style A record [RFC1706]
+ */
+ TYPE_NSAP("nsap", 22),
+ /**
+ *
+ */
+ TYPE_NSAP_PTR("nsap-otr", 23),
+ /**
+ * for security signature [RFC2931]
+ */
+ TYPE_SIG("sig", 24),
+ /**
+ * for security key [RFC2535]
+ */
+ TYPE_KEY("key", 25),
+ /**
+ * X.400 mail mapping information [RFC2163]
+ */
+ TYPE_PX("px", 26),
+ /**
+ * Geographical Position [RFC1712]
+ */
+ TYPE_GPOS("gpos", 27),
+ /**
+ * IP6 Address [Thomson]
+ */
+ TYPE_AAAA("aaaa", 28),
+ /**
+ * Location Information [Vixie]
+ */
+ TYPE_LOC("loc", 29),
+ /**
+ * Next Domain - OBSOLETE [RFC2535, RFC3755]
+ */
+ TYPE_NXT("nxt", 30),
+ /**
+ * Endpoint Identifier [Patton]
+ */
+ TYPE_EID("eid", 31),
+ /**
+ * Nimrod Locator [Patton]
+ */
+ TYPE_NIMLOC("nimloc", 32),
+ /**
+ * Server Selection [RFC2782]
+ */
+ TYPE_SRV("srv", 33),
+ /**
+ * ATM Address [Dobrowski]
+ */
+ TYPE_ATMA("atma", 34),
+ /**
+ * Naming Authority Pointer [RFC2168, RFC2915]
+ */
+ TYPE_NAPTR("naptr", 35),
+ /**
+ * Key Exchanger [RFC2230]
+ */
+ TYPE_KX("kx", 36),
+ /**
+ * CERT [RFC2538]
+ */
+ TYPE_CERT("cert", 37),
+ /**
+ * A6 [RFC2874]
+ */
+ TYPE_A6("a6", 38),
+ /**
+ * DNAME [RFC2672]
+ */
+ TYPE_DNAME("dname", 39),
+ /**
+ * SINK [Eastlake]
+ */
+ TYPE_SINK("sink", 40),
+ /**
+ * OPT [RFC2671]
+ */
+ TYPE_OPT("opt", 41),
+ /**
+ * APL [RFC3123]
+ */
+ TYPE_APL("apl", 42),
+ /**
+ * Delegation Signer [RFC3658]
+ */
+ TYPE_DS("ds", 43),
+ /**
+ * SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt]
+ */
+ TYPE_SSHFP("sshfp", 44),
+ /**
+ * RRSIG [RFC3755]
+ */
+ TYPE_RRSIG("rrsig", 46),
+ /**
+ * NSEC [RFC3755]
+ */
+ TYPE_NSEC("nsec", 47),
+ /**
+ * DNSKEY [RFC3755]
+ */
+ TYPE_DNSKEY("dnskey", 48),
+ /**
+ * [IANA-Reserved]
+ */
+ TYPE_UINFO("uinfo", 100),
+ /**
+ * [IANA-Reserved]
+ */
+ TYPE_UID("uid", 101),
+ /**
+ * [IANA-Reserved]
+ */
+ TYPE_GID("gid", 102),
+ /**
+ * [IANA-Reserved]
+ */
+ TYPE_UNSPEC("unspec", 103),
+ /**
+ * Transaction Key [RFC2930]
+ */
+ TYPE_TKEY("tkey", 249),
+ /**
+ * Transaction Signature [RFC2845]
+ */
+ TYPE_TSIG("tsig", 250),
+ /**
+ * Incremental transfer [RFC1995]
+ */
+ TYPE_IXFR("ixfr", 251),
+ /**
+ * Transfer of an entire zone [RFC1035]
+ */
+ TYPE_AXFR("axfr", 252),
+ /**
+ * Mailbox-related records (MB, MG or MR) [RFC1035]
+ */
+ TYPE_MAILA("mails", 253),
+ /**
+ * Mail agent RRs (Obsolete - see MX) [RFC1035]
+ */
+ TYPE_MAILB("mailb", 254),
+ /**
+ * Request for all records [RFC1035]
+ */
+ TYPE_ANY("any", 255);
+
+ private static Logger logger = Logger.getLogger(DNSRecordType.class.getName());
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSRecordType(String name, int index)
+ {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName()
+ {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue()
+ {
+ return _index;
+ }
+
+ /**
+ * @param name
+ * @return type for name
+ */
+ public static DNSRecordType typeForName(String name)
+ {
+ if (name != null)
+ {
+ String aName = name.toLowerCase();
+ for (DNSRecordType aType : DNSRecordType.values())
+ {
+ if (aType._externalName.equals(aName))
+ return aType;
+ }
+ }
+ logger.severe("Could not find record type for name: " + name);
+ return TYPE_IGNORE;
+ }
+
+ /**
+ * @param index
+ * @return type for name
+ */
+ public static DNSRecordType typeForIndex(int index)
+ {
+ for (DNSRecordType aType : DNSRecordType.values())
+ {
+ if (aType._index == index)
+ return aType;
+ }
+ logger.severe("Could not find record type for index: " + index);
+ return TYPE_IGNORE;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.name() + " index " + this.indexValue();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSResultCode.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSResultCode.java
new file mode 100644
index 000000000..bfc219ab2
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSResultCode.java
@@ -0,0 +1,161 @@
+/**
+ *
+ */
+package javax.jmdns.impl.constants;
+
+/**
+ * DNS result code.
+ *
+ * @version %I%, %G%
+ * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
+ */
+public enum DNSResultCode
+{
+ /**
+ * Token
+ */
+ Unknown("Unknown", 65535),
+ /**
+ * No Error [RFC1035]
+ */
+ NoError("No Error", 0),
+ /**
+ * Format Error [RFC1035]
+ */
+ FormErr("Format Error", 1),
+ /**
+ * Server Failure [RFC1035]
+ */
+ ServFail("Server Failure", 2),
+ /**
+ * Non-Existent Domain [RFC1035]
+ */
+ NXDomain("Non-Existent Domain", 3),
+ /**
+ * Not Implemented [RFC1035]
+ */
+ NotImp("Not Implemented", 4),
+ /**
+ * Query Refused [RFC1035]
+ */
+ Refused("Query Refused", 5),
+ /**
+ * Name Exists when it should not [RFC2136]
+ */
+ YXDomain("Name Exists when it should not", 6),
+ /**
+ * RR Set Exists when it should not [RFC2136]
+ */
+ YXRRSet("RR Set Exists when it should not", 7),
+ /**
+ * RR Set that should exist does not [RFC2136]
+ */
+ NXRRSet("RR Set that should exist does not", 8),
+ /**
+ * Server Not Authoritative for zone [RFC2136]]
+ */
+ NotAuth("Server Not Authoritative for zone", 9),
+ /**
+ * Name not contained in zone [RFC2136]
+ */
+ NotZone("NotZone Name not contained in zone", 10),
+
+ ;
+
+ // 0 NoError No Error [RFC1035]
+ // 1 FormErr Format Error [RFC1035]
+ // 2 ServFail Server Failure [RFC1035]
+ // 3 NXDomain Non-Existent Domain [RFC1035]
+ // 4 NotImp Not Implemented [RFC1035]
+ // 5 Refused Query Refused [RFC1035]
+ // 6 YXDomain Name Exists when it should not [RFC2136]
+ // 7 YXRRSet RR Set Exists when it should not [RFC2136]
+ // 8 NXRRSet RR Set that should exist does not [RFC2136]
+ // 9 NotAuth Server Not Authoritative for zone [RFC2136]
+ // 10 NotZone Name not contained in zone [RFC2136]
+ // 11-15 Unassigned
+ // 16 BADVERS Bad OPT Version [RFC2671]
+ // 16 BADSIG TSIG Signature Failure [RFC2845]
+ // 17 BADKEY Key not recognized [RFC2845]
+ // 18 BADTIME Signature out of time window [RFC2845]
+ // 19 BADMODE Bad TKEY Mode [RFC2930]
+ // 20 BADNAME Duplicate key name [RFC2930]
+ // 21 BADALG Algorithm not supported [RFC2930]
+ // 22 BADTRUNC Bad Truncation [RFC4635]
+ // 23-3840 Unassigned
+ // 3841-4095 Reserved for Private Use [RFC5395]
+ // 4096-65534 Unassigned
+ // 65535 Reserved, can be allocated by Standards Action [RFC5395]
+
+ /**
+ * DNS Result Code types are encoded on the last 4 bits
+ */
+ final static int RCode_MASK = 0x0F;
+ /**
+ * DNS Extended Result Code types are encoded on the first 8 bits
+ */
+ final static int ExtendedRCode_MASK = 0xFF;
+
+ private final String _externalName;
+
+ private final int _index;
+
+ DNSResultCode(String name, int index)
+ {
+ _externalName = name;
+ _index = index;
+ }
+
+ /**
+ * Return the string representation of this type
+ *
+ * @return String
+ */
+ public String externalName()
+ {
+ return _externalName;
+ }
+
+ /**
+ * Return the numeric value of this type
+ *
+ * @return String
+ */
+ public int indexValue()
+ {
+ return _index;
+ }
+
+ /**
+ * @param flags
+ * @return label
+ */
+ public static DNSResultCode resultCodeForFlags(int flags)
+ {
+ int maskedIndex = flags & RCode_MASK;
+ for (DNSResultCode aCode : DNSResultCode.values())
+ {
+ if (aCode._index == maskedIndex)
+ return aCode;
+ }
+ return Unknown;
+ }
+
+ public static DNSResultCode resultCodeForFlags(int flags, int extendedRCode)
+ {
+ int maskedIndex = ((extendedRCode >> 28) & ExtendedRCode_MASK) | (flags & RCode_MASK);
+ for (DNSResultCode aCode : DNSResultCode.values())
+ {
+ if (aCode._index == maskedIndex)
+ return aCode;
+ }
+ return Unknown;
+ }
+
+ @Override
+ public String toString()
+ {
+ return this.name() + " index " + this.indexValue();
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSState.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSState.java
new file mode 100644
index 000000000..05d815c47
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/constants/DNSState.java
@@ -0,0 +1,193 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.constants;
+
+/**
+ * DNSState defines the possible states for services registered with JmDNS.
+ *
+ * @version %I%, %G%
+ * @author Werner Randelshofer, Rick Blair, Pierre Frisch
+ */
+public enum DNSState
+{
+
+ /**
+ *
+ */
+ PROBING_1("probing 1", StateClass.probing),
+ /**
+ *
+ */
+ PROBING_2("probing 2", StateClass.probing),
+ /**
+ *
+ */
+ PROBING_3("probing 3", StateClass.probing),
+ /**
+ *
+ */
+ ANNOUNCING_1("announcing 1", StateClass.announcing),
+ /**
+ *
+ */
+ ANNOUNCING_2("announcing 2", StateClass.announcing),
+ /**
+ *
+ */
+ ANNOUNCED("announced", StateClass.announced),
+ /**
+ *
+ */
+ CANCELING_1("canceling 1", StateClass.canceling),
+ /**
+ *
+ */
+ CANCELING_2("canceling 2", StateClass.canceling),
+ /**
+ *
+ */
+ CANCELING_3("canceling 3", StateClass.canceling),
+ /**
+ *
+ */
+ CANCELED("canceled", StateClass.canceled);
+
+ private enum StateClass
+ {
+ probing, announcing, announced, canceling, canceled
+ }
+
+ // private static Logger logger = Logger.getLogger(DNSState.class.getName());
+
+ private final String _name;
+
+ private final StateClass _state;
+
+ private DNSState(String name, StateClass state)
+ {
+ _name = name;
+ _state = state;
+ }
+
+ @Override
+ public final String toString()
+ {
+ return _name;
+ }
+
+ /**
+ * Returns the next advanced state.<br/>
+ * In general, this advances one step in the following sequence: PROBING_1, PROBING_2, PROBING_3, ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED.<br/>
+ * or CANCELING_1, CANCELING_2, CANCELING_3, CANCELED Does not advance for ANNOUNCED and CANCELED state.
+ *
+ * @return next state
+ */
+ public final DNSState advance()
+ {
+ switch (this)
+ {
+ case PROBING_1:
+ return PROBING_2;
+ case PROBING_2:
+ return PROBING_3;
+ case PROBING_3:
+ return ANNOUNCING_1;
+ case ANNOUNCING_1:
+ return ANNOUNCING_2;
+ case ANNOUNCING_2:
+ return ANNOUNCED;
+ case ANNOUNCED:
+ return ANNOUNCED;
+ case CANCELING_1:
+ return CANCELING_2;
+ case CANCELING_2:
+ return CANCELING_3;
+ case CANCELING_3:
+ return CANCELED;
+ case CANCELED:
+ return CANCELED;
+ }
+ // This is just to keep the compiler happy as we have covered all cases before.
+ return this;
+ }
+
+ /**
+ * Returns to the next reverted state. All states except CANCELED revert to PROBING_1. Status CANCELED does not revert.
+ *
+ * @return reverted state
+ */
+ public final DNSState revert()
+ {
+ switch (this)
+ {
+ case PROBING_1:
+ case PROBING_2:
+ case PROBING_3:
+ case ANNOUNCING_1:
+ case ANNOUNCING_2:
+ case ANNOUNCED:
+ return PROBING_1;
+ case CANCELING_1:
+ case CANCELING_2:
+ case CANCELING_3:
+ return CANCELING_1;
+ case CANCELED:
+ return CANCELED;
+ }
+ // This is just to keep the compiler happy as we have covered all cases before.
+ return this;
+ }
+
+ /**
+ * Returns true, if this is a probing state.
+ *
+ * @return <code>true</code> if probing state, <code>false</code> otherwise
+ */
+ public final boolean isProbing()
+ {
+ return _state == StateClass.probing;
+ }
+
+ /**
+ * Returns true, if this is an announcing state.
+ *
+ * @return <code>true</code> if announcing state, <code>false</code> otherwise
+ */
+ public final boolean isAnnouncing()
+ {
+ return _state == StateClass.announcing;
+ }
+
+ /**
+ * Returns true, if this is an announced state.
+ *
+ * @return <code>true</code> if announced state, <code>false</code> otherwise
+ */
+ public final boolean isAnnounced()
+ {
+ return _state == StateClass.announced;
+ }
+
+ /**
+ * Returns true, if this is a canceling state.
+ *
+ * @return <code>true</code> if canceling state, <code>false</code> otherwise
+ */
+ public final boolean isCanceling()
+ {
+ return _state == StateClass.canceling;
+ }
+
+ /**
+ * Returns true, if this is a canceled state.
+ *
+ * @return <code>true</code> if canceled state, <code>false</code> otherwise
+ */
+ public final boolean isCanceled()
+ {
+ return _state == StateClass.canceled;
+ }
+
+} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Announcer.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Announcer.java
deleted file mode 100644
index 5de471267..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Announcer.java
+++ /dev/null
@@ -1,159 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSState;
-import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
-
-/**
- * The Announcer sends an accumulated query of all announces, and advances
- * the state of all serviceInfos, for which it has sent an announce.
- * The Announcer also sends announcements and advances the state of JmDNS itself.
- * <p/>
- * When the announcer has run two times, it finishes.
- */
-public class Announcer extends TimerTask
-{
-// static Logger logger = Logger.getLogger(Announcer.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
- /**
- * The state of the announcer.
- */
- DNSState taskState = DNSState.ANNOUNCING_1;
-
- public Announcer(JmDNSImpl jmDNSImpl)
- {
- this.jmDNSImpl = jmDNSImpl;
- // Associate host to this, if it needs announcing
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCING_1)
- {
- this.jmDNSImpl.setTask(this);
- }
- // Associate services to this, if they need announcing
- synchronized (this.jmDNSImpl)
- {
- for (Iterator s = this.jmDNSImpl.getServices().values().iterator(); s.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) s.next();
- if (info.getState() == DNSState.ANNOUNCING_1)
- {
- info.setTask(this);
- }
- }
- }
- }
-
- public void start(Timer timer)
- {
- timer.schedule(this, DNSConstants.ANNOUNCE_WAIT_INTERVAL, DNSConstants.ANNOUNCE_WAIT_INTERVAL);
- }
-
- public boolean cancel()
- {
- // Remove association from host to this
- if (this.jmDNSImpl.getTask() == this)
- {
- this.jmDNSImpl.setTask(null);
- }
-
- // Remove associations from services to this
- synchronized (this.jmDNSImpl)
- {
- for (Iterator i = this.jmDNSImpl.getServices().values().iterator(); i.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
- if (info.getTask() == this)
- {
- info.setTask(null);
- }
- }
- }
-
- return super.cancel();
- }
-
- public void run()
- {
- DNSOutgoing out = null;
- try
- {
- // send probes for JmDNS itself
- if (this.jmDNSImpl.getState() == taskState)
- {
- if (out == null)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- }
- this.jmDNSImpl.getLocalHost().addAddressRecords(out, false);
- this.jmDNSImpl.advanceState();
- }
- // send announces for services
- // Defensively copy the services into a local list,
- // to prevent race conditions with methods registerService
- // and unregisterService.
- List list;
- synchronized (this.jmDNSImpl)
- {
- list = new ArrayList(this.jmDNSImpl.getServices().values());
- }
- for (Iterator i = list.iterator(); i.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
- synchronized (info)
- {
- if (info.getState() == taskState && info.getTask() == this)
- {
- info.advanceState();
-// logger.finer("run() JmDNS announcing " + info.getQualifiedName() + " state " + info.getState());
- if (out == null)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- }
- info.addAnswers(out, DNSConstants.DNS_TTL, this.jmDNSImpl.getLocalHost());
- }
- }
- }
- if (out != null)
- {
-// logger.finer("run() JmDNS announcing #" + taskState);
- this.jmDNSImpl.send(out);
- }
- else
- {
- // If we have nothing to send, another timer taskState ahead
- // of us has done the job for us. We can cancel.
- cancel();
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
- }
-
- taskState = taskState.advance();
- if (!taskState.isAnnouncing())
- {
- cancel();
-
- this.jmDNSImpl.startRenewer();
- }
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Canceler.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Canceler.java
deleted file mode 100644
index 23997281f..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Canceler.java
+++ /dev/null
@@ -1,122 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.util.Collection;
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSQuestion;
-import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
-
-/**
- * The Canceler sends two announces with TTL=0 for the specified services.
- */
-public class Canceler extends TimerTask
-{
-// static Logger logger = Logger.getLogger(Canceler.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
- /**
- * Counts the number of announces being sent.
- */
- int count = 0;
- /**
- * The services that need cancelling.
- * Note: We have to use a local variable here, because the services
- * that are canceled, are removed immediately from variable JmDNS.services.
- */
- private ServiceInfoImpl[] infos;
- /**
- * We call notifyAll() on the lock object, when we have canceled the
- * service infos.
- * This is used by method JmDNS.unregisterService() and
- * JmDNS.unregisterAllServices, to ensure that the JmDNS
- * socket stays open until the Canceler has canceled all services.
- * <p/>
- * Note: We need this lock, because ServiceInfos do the transition from
- * state ANNOUNCED to state CANCELED before we get here. We could get
- * rid of this lock, if we added a state named CANCELLING to DNSState.
- */
- private Object lock;
- int ttl = 0;
-
- public Canceler(JmDNSImpl jmDNSImpl, ServiceInfoImpl info, Object lock)
- {
- this.jmDNSImpl = jmDNSImpl;
- this.infos = new ServiceInfoImpl[]{info};
- this.lock = lock;
- this.jmDNSImpl.addListener(info, new DNSQuestion(info.getQualifiedName(), DNSConstants.TYPE_ANY, DNSConstants.CLASS_IN));
- }
-
- public Canceler(JmDNSImpl jmDNSImpl, ServiceInfoImpl[] infos, Object lock)
- {
- this.jmDNSImpl = jmDNSImpl;
- this.infos = infos;
- this.lock = lock;
- }
-
- public Canceler(JmDNSImpl jmDNSImpl, Collection infos, Object lock)
- {
- this.jmDNSImpl = jmDNSImpl;
- this.infos = (ServiceInfoImpl[]) infos.toArray(new ServiceInfoImpl[infos.size()]);
- this.lock = lock;
- }
-
- public void start(Timer timer)
- {
- timer.schedule(this, 0, DNSConstants.ANNOUNCE_WAIT_INTERVAL);
- }
-
- public void run()
- {
- try
- {
- if (++count < 3)
- {
-// logger.finer("run() JmDNS canceling service");
- // announce the service
- //long now = System.currentTimeMillis();
- DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- for (int i = 0; i < infos.length; i++)
- {
- ServiceInfoImpl info = infos[i];
- info.addAnswers(out, ttl, this.jmDNSImpl.getLocalHost());
-
- this.jmDNSImpl.getLocalHost().addAddressRecords(out, false);
- }
- this.jmDNSImpl.send(out);
- }
- else
- {
- // After three successful announcements, we are finished.
- synchronized (lock)
- {
- this.jmDNSImpl.setClosed(true);
- lock.notifyAll();
- }
- this.cancel();
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- synchronized (lock)
- {
- lock.notifyAll();
- }
- this.cancel();
- this.jmDNSImpl.recover();
- }
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/DNSTask.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/DNSTask.java
new file mode 100644
index 000000000..59d3b33dc
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/DNSTask.java
@@ -0,0 +1,249 @@
+//Licensed under Apache License version 2.0
+package javax.jmdns.impl.tasks;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import javax.jmdns.impl.DNSIncoming;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+
+/**
+ * This is the root class for all task scheduled by the timer in JmDNS.
+ *
+ * @version %I%, %G%
+ * @author Pierre Frisch
+ */
+public abstract class DNSTask extends TimerTask
+{
+
+ /**
+ *
+ */
+ private final JmDNSImpl _jmDNSImpl;
+
+ /**
+ * @param jmDNSImpl
+ */
+ protected DNSTask(JmDNSImpl jmDNSImpl)
+ {
+ super();
+ this._jmDNSImpl = jmDNSImpl;
+ }
+
+ /**
+ * Return the DNS associated with this task.
+ *
+ * @return associated DNS
+ */
+ public JmDNSImpl getDns()
+ {
+ return _jmDNSImpl;
+ }
+
+ /**
+ * Start this task.
+ *
+ * @param timer
+ * task timer.
+ */
+ public abstract void start(Timer timer);
+
+ /**
+ * Return this task name.
+ *
+ * @return task name
+ */
+ public abstract String getName();
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return this.getName();
+ }
+
+ /**
+ * Add a question to the message.
+ *
+ * @param out
+ * outgoing message
+ * @param rec
+ * DNS question
+ * @return outgoing message for the next question
+ * @throws IOException
+ */
+ public DNSOutgoing addQuestion(DNSOutgoing out, DNSQuestion rec) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ try
+ {
+ newOut.addQuestion(rec);
+ }
+ catch (final IOException e)
+ {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addQuestion(rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an answer if it is not suppressed.
+ *
+ * @param out
+ * outgoing message
+ * @param in
+ * incoming request
+ * @param rec
+ * DNS record answer
+ * @return outgoing message for the next answer
+ * @throws IOException
+ */
+ public DNSOutgoing addAnswer(DNSOutgoing out, DNSIncoming in, DNSRecord rec) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ try
+ {
+ newOut.addAnswer(in, rec);
+ }
+ catch (final IOException e)
+ {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAnswer(in, rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an answer to the message.
+ *
+ * @param out
+ * outgoing message
+ * @param rec
+ * DNS record answer
+ * @param now
+ * @return outgoing message for the next answer
+ * @throws IOException
+ */
+ public DNSOutgoing addAnswer(DNSOutgoing out, DNSRecord rec, long now) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ try
+ {
+ newOut.addAnswer(rec, now);
+ }
+ catch (final IOException e)
+ {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAnswer(rec, now);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an authoritative answer to the message.
+ *
+ * @param out
+ * outgoing message
+ * @param rec
+ * DNS record answer
+ * @return outgoing message for the next answer
+ * @throws IOException
+ */
+ public DNSOutgoing addAuthoritativeAnswer(DNSOutgoing out, DNSRecord rec) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ try
+ {
+ newOut.addAuthorativeAnswer(rec);
+ }
+ catch (final IOException e)
+ {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAuthorativeAnswer(rec);
+ }
+ return newOut;
+ }
+
+ /**
+ * Add an additional answer to the record. Omit if there is no room.
+ *
+ * @param out
+ * outgoing message
+ * @param in
+ * incoming request
+ * @param rec
+ * DNS record answer
+ * @return outgoing message for the next answer
+ * @throws IOException
+ */
+ public DNSOutgoing addAdditionalAnswer(DNSOutgoing out, DNSIncoming in, DNSRecord rec) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ try
+ {
+ newOut.addAdditionalAnswer(in, rec);
+ }
+ catch (final IOException e)
+ {
+ int flags = newOut.getFlags();
+ boolean multicast = newOut.isMulticast();
+ int maxUDPPayload = newOut.getMaxUDPPayload();
+ int id = newOut.getId();
+
+ newOut.setFlags(flags | DNSConstants.FLAGS_TC);
+ newOut.setId(id);
+ this._jmDNSImpl.send(newOut);
+
+ newOut = new DNSOutgoing(flags, multicast, maxUDPPayload);
+ newOut.addAdditionalAnswer(in, rec);
+ }
+ return newOut;
+ }
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Prober.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Prober.java
deleted file mode 100644
index af2ed6344..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Prober.java
+++ /dev/null
@@ -1,197 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSQuestion;
-import javax.jmdns.impl.DNSRecord;
-import javax.jmdns.impl.DNSState;
-import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
-
-/**
- * The Prober sends three consecutive probes for all service infos
- * that needs probing as well as for the host name.
- * The state of each service info of the host name is advanced, when a probe has
- * been sent for it.
- * When the prober has run three times, it launches an Announcer.
- * <p/>
- * If a conflict during probes occurs, the affected service infos (and affected
- * host name) are taken away from the prober. This eventually causes the prober
- * tho cancel itself.
- */
-public class Prober extends TimerTask
-{
-// static Logger logger = Logger.getLogger(Prober.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
- /**
- * The state of the prober.
- */
- DNSState taskState = DNSState.PROBING_1;
-
- public Prober(JmDNSImpl jmDNSImpl)
- {
- this.jmDNSImpl = jmDNSImpl;
- // Associate the host name to this, if it needs probing
- if (this.jmDNSImpl.getState() == DNSState.PROBING_1)
- {
- this.jmDNSImpl.setTask(this);
- }
- // Associate services to this, if they need probing
- synchronized (this.jmDNSImpl)
- {
- for (Iterator iterator = this.jmDNSImpl.getServices().values().iterator(); iterator.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) iterator.next();
- if (info.getState() == DNSState.PROBING_1)
- {
- info.setTask(this);
- }
- }
- }
- }
-
-
- public void start(Timer timer)
- {
- long now = System.currentTimeMillis();
- if (now - this.jmDNSImpl.getLastThrottleIncrement() < DNSConstants.PROBE_THROTTLE_COUNT_INTERVAL)
- {
- this.jmDNSImpl.setThrottle(this.jmDNSImpl.getThrottle() + 1);
- }
- else
- {
- this.jmDNSImpl.setThrottle(1);
- }
- this.jmDNSImpl.setLastThrottleIncrement(now);
-
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED && this.jmDNSImpl.getThrottle() < DNSConstants.PROBE_THROTTLE_COUNT)
- {
- timer.schedule(this, JmDNSImpl.getRandom().nextInt(1 + DNSConstants.PROBE_WAIT_INTERVAL), DNSConstants.PROBE_WAIT_INTERVAL);
- }
- else
- {
- timer.schedule(this, DNSConstants.PROBE_CONFLICT_INTERVAL, DNSConstants.PROBE_CONFLICT_INTERVAL);
- }
- }
-
- public boolean cancel()
- {
- // Remove association from host name to this
- if (this.jmDNSImpl.getTask() == this)
- {
- this.jmDNSImpl.setTask(null);
- }
-
- // Remove associations from services to this
- synchronized (this.jmDNSImpl)
- {
- for (Iterator i = this.jmDNSImpl.getServices().values().iterator(); i.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
- if (info.getTask() == this)
- {
- info.setTask(null);
- }
- }
- }
-
- return super.cancel();
- }
-
- public void run()
- {
- synchronized (this.jmDNSImpl.getIoLock())
- {
- DNSOutgoing out = null;
- try
- {
- // send probes for JmDNS itself
- if (this.jmDNSImpl.getState() == taskState && this.jmDNSImpl.getTask() == this)
- {
- if (out == null)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
- }
- out.addQuestion(new DNSQuestion(this.jmDNSImpl.getLocalHost().getName(), DNSConstants.TYPE_ANY, DNSConstants.CLASS_IN));
-
- this.jmDNSImpl.getLocalHost().addAddressRecords(out, true);
- this.jmDNSImpl.advanceState();
- }
- // send probes for services
- // Defensively copy the services into a local list,
- // to prevent race conditions with methods registerService
- // and unregisterService.
- List list;
- synchronized (this.jmDNSImpl)
- {
- list = new LinkedList(this.jmDNSImpl.getServices().values());
- }
- for (Iterator i = list.iterator(); i.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
-
- synchronized (info)
- {
- if (info.getState() == taskState && info.getTask() == this)
- {
- info.advanceState();
-// logger.fine("run() JmDNS probing " + info.getQualifiedName() + " state " + info.getState());
- if (out == null)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
- out.addQuestion(new DNSQuestion(info.getQualifiedName(), DNSConstants.TYPE_ANY, DNSConstants.CLASS_IN));
- }
- // the "unique" flag should be not set here because these answers haven't been proven unique yet
- // this means the record will not exactly match the announcement record
- out.addAuthorativeAnswer(new DNSRecord.Service(info.getQualifiedName(),
- DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL,
- info.getPriority(), info.getWeight(), info.getPort(), this.jmDNSImpl.getLocalHost().getName()));
- }
- }
- }
- if (out != null)
- {
-// logger.finer("run() JmDNS probing #" + taskState);
- this.jmDNSImpl.send(out);
- }
- else
- {
- // If we have nothing to send, another timer taskState ahead
- // of us has done the job for us. We can cancel.
- cancel();
- return;
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
- }
-
- taskState = taskState.advance();
- if (!taskState.isProbing())
- {
- cancel();
-
- this.jmDNSImpl.startAnnouncer();
- }
- }
- }
-
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/RecordReaper.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/RecordReaper.java
index 903e74a7e..7e59bf631 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/RecordReaper.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/RecordReaper.java
@@ -4,80 +4,92 @@
package javax.jmdns.impl.tasks;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Logger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-import javax.jmdns.impl.DNSCache;
-import javax.jmdns.impl.DNSConstants;
+import javax.jmdns.impl.DNSEntry;
import javax.jmdns.impl.DNSRecord;
-import javax.jmdns.impl.DNSState;
import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.JmDNSImpl.Operation;
+import javax.jmdns.impl.constants.DNSConstants;
/**
- * Periodicaly removes expired entries from the cache.
+ * Periodically removes expired entries from the cache.
*/
-public class RecordReaper extends TimerTask
+public class RecordReaper extends DNSTask
{
-// static Logger logger = Logger.getLogger(RecordReaper.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
+ static Logger logger = Logger.getLogger(RecordReaper.class.getName());
/**
* @param jmDNSImpl
*/
public RecordReaper(JmDNSImpl jmDNSImpl)
{
- this.jmDNSImpl = jmDNSImpl;
+ super(jmDNSImpl);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return "RecordReaper(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
public void start(Timer timer)
{
- timer.schedule(this, DNSConstants.RECORD_REAPER_INTERVAL, DNSConstants.RECORD_REAPER_INTERVAL);
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled())
+ {
+ timer.schedule(this, DNSConstants.RECORD_REAPER_INTERVAL, DNSConstants.RECORD_REAPER_INTERVAL);
+ }
}
+ @Override
public void run()
{
- synchronized (this.jmDNSImpl)
+ if (this.getDns().isCanceling() || this.getDns().isCanceled())
{
- if (this.jmDNSImpl.getState() == DNSState.CANCELED)
- {
- return;
- }
-// logger.finest("run() JmDNS reaping cache");
+ return;
+ }
+ if (logger.isLoggable(Level.FINEST))
+ {
+ logger.finest(this.getName() + ".run() JmDNS reaping cache");
+ }
- // Remove expired answers from the cache
- // -------------------------------------
- // To prevent race conditions, we defensively copy all cache
- // entries into a list.
- List list = new ArrayList();
- synchronized (this.jmDNSImpl.getCache())
+ // Remove expired answers from the cache
+ // -------------------------------------
+ long now = System.currentTimeMillis();
+ for (DNSEntry entry : this.getDns().getCache().allValues())
+ {
+ try
{
- for (Iterator i = this.jmDNSImpl.getCache().iterator(); i.hasNext();)
+ DNSRecord record = (DNSRecord) entry;
+ if (record.isStale(now))
{
- for (DNSCache.CacheNode n = (DNSCache.CacheNode) i.next(); n != null; n = n.next())
- {
- list.add(n.getValue());
- }
+ // we should query for the record we care about i.e. those in the service collectors
+ this.getDns().renewServiceCollector(record);
}
- }
- // Now, we remove them.
- long now = System.currentTimeMillis();
- for (Iterator i = list.iterator(); i.hasNext();)
- {
- DNSRecord c = (DNSRecord) i.next();
- if (c.isExpired(now))
+ if (record.isExpired(now))
{
- this.jmDNSImpl.updateRecord(now, c);
- this.jmDNSImpl.getCache().remove(c);
+ this.getDns().updateRecord(now, record, Operation.Remove);
+ this.getDns().getCache().removeDNSEntry(record);
}
}
+ catch (Exception exception)
+ {
+ logger.log(Level.SEVERE, this.getName() + ".Error while reaping records: " + entry, exception);
+ logger.severe(this.getDns().toString());
+ }
}
}
} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Renewer.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Renewer.java
deleted file mode 100644
index 4e63796b5..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Renewer.java
+++ /dev/null
@@ -1,154 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSState;
-import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
-
-/**
- * The Renewer is there to send renewal announcment when the record expire for ours infos.
- */
-public class Renewer extends TimerTask
-{
-// static Logger logger = Logger.getLogger(Renewer.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
- /**
- * The state of the announcer.
- */
- DNSState taskState = DNSState.ANNOUNCED;
-
- public Renewer(JmDNSImpl jmDNSImpl)
- {
- this.jmDNSImpl = jmDNSImpl;
- // Associate host to this, if it needs renewal
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED)
- {
- this.jmDNSImpl.setTask(this);
- }
- // Associate services to this, if they need renewal
- synchronized (this.jmDNSImpl)
- {
- for (Iterator s = this.jmDNSImpl.getServices().values().iterator(); s.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) s.next();
- if (info.getState() == DNSState.ANNOUNCED)
- {
- info.setTask(this);
- }
- }
- }
- }
-
- public void start(Timer timer)
- {
- timer.schedule(this, DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL, DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL);
- }
-
- public boolean cancel()
- {
- // Remove association from host to this
- if (this.jmDNSImpl.getTask() == this)
- {
- this.jmDNSImpl.setTask(null);
- }
-
- // Remove associations from services to this
- synchronized (this.jmDNSImpl)
- {
- for (Iterator i = this.jmDNSImpl.getServices().values().iterator(); i.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
- if (info.getTask() == this)
- {
- info.setTask(null);
- }
- }
- }
-
- return super.cancel();
- }
-
- public void run()
- {
- DNSOutgoing out = null;
- try
- {
- // send probes for JmDNS itself
- if (this.jmDNSImpl.getState() == taskState)
- {
- if (out == null)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- }
- this.jmDNSImpl.getLocalHost().addAddressRecords(out, false);
- this.jmDNSImpl.advanceState();
- }
- // send announces for services
- // Defensively copy the services into a local list,
- // to prevent race conditions with methods registerService
- // and unregisterService.
- List list;
- synchronized (this.jmDNSImpl)
- {
- list = new ArrayList(this.jmDNSImpl.getServices().values());
- }
- for (Iterator i = list.iterator(); i.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) i.next();
- synchronized (info)
- {
- if (info.getState() == taskState && info.getTask() == this)
- {
- info.advanceState();
-// logger.finer("run() JmDNS announced " + info.getQualifiedName() + " state " + info.getState());
- if (out == null)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA);
- }
- info.addAnswers(out, DNSConstants.DNS_TTL, this.jmDNSImpl.getLocalHost());
- }
- }
- }
- if (out != null)
- {
-// logger.finer("run() JmDNS announced");
- this.jmDNSImpl.send(out);
- }
- else
- {
- // If we have nothing to send, another timer taskState ahead
- // of us has done the job for us. We can cancel.
- cancel();
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
- }
-
- taskState = taskState.advance();
- if (!taskState.isAnnounced())
- {
- cancel();
-
- }
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Responder.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Responder.java
index 82eef09f9..069a78941 100644
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Responder.java
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/Responder.java
@@ -4,287 +4,178 @@
package javax.jmdns.impl.tasks;
-import java.net.InetAddress;
import java.util.HashSet;
-import java.util.Iterator;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
+import java.util.Set;
+import java.util.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSEntry;
import javax.jmdns.impl.DNSIncoming;
import javax.jmdns.impl.DNSOutgoing;
import javax.jmdns.impl.DNSQuestion;
import javax.jmdns.impl.DNSRecord;
-import javax.jmdns.impl.DNSState;
import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
/**
- * The Responder sends a single answer for the specified service infos
- * and for the host name.
+ * The Responder sends a single answer for the specified service infos and for the host name.
*/
-public class Responder extends TimerTask
+public class Responder extends DNSTask
{
-// static Logger logger = Logger.getLogger(Responder.class.getName());
+ static Logger logger = Logger.getLogger(Responder.class.getName());
/**
- *
+ *
*/
- private final JmDNSImpl jmDNSImpl;
- private DNSIncoming in;
- private InetAddress addr;
- private int port;
+ private DNSIncoming _in;
- public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, InetAddress addr, int port)
+ /**
+ *
+ */
+ private boolean _unicast;
+
+ public Responder(JmDNSImpl jmDNSImpl, DNSIncoming in, int port)
+ {
+ super(jmDNSImpl);
+ this._in = in;
+ this._unicast = (port != DNSConstants.MDNS_PORT);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
{
- this.jmDNSImpl = jmDNSImpl;
- this.in = in;
- this.addr = addr;
- this.port = port;
+ return "Responder(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
}
- public void start()
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
{
- // According to draft-cheshire-dnsext-multicastdns.txt
- // chapter "8 Responding":
- // We respond immediately if we know for sure, that we are
- // the only one who can respond to the query.
+ return super.toString() + " incomming: " + _in;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer)
+ {
+ // According to draft-cheshire-dnsext-multicastdns.txt chapter "7 Responding":
+ // We respond immediately if we know for sure, that we are the only one who can respond to the query.
// In all other cases, we respond within 20-120 ms.
//
- // According to draft-cheshire-dnsext-multicastdns.txt
- // chapter "7.2 Multi-Packet Known Answer Suppression":
+ // According to draft-cheshire-dnsext-multicastdns.txt chapter "6.2 Multi-Packet Known Answer Suppression":
// We respond after 20-120 ms if the query is truncated.
boolean iAmTheOnlyOne = true;
- for (Iterator i = in.getQuestions().iterator(); i.hasNext();)
+ for (DNSQuestion question : _in.getQuestions())
{
- DNSEntry entry = (DNSEntry) i.next();
- if (entry instanceof DNSQuestion)
+ if (logger.isLoggable(Level.FINEST))
{
- DNSQuestion q = (DNSQuestion) entry;
-// logger.finest("start() question=" + q);
- iAmTheOnlyOne &= (q.getType() == DNSConstants.TYPE_SRV
- || q.getType() == DNSConstants.TYPE_TXT
- || q.getType() == DNSConstants.TYPE_A
- || q.getType() == DNSConstants.TYPE_AAAA
- || this.jmDNSImpl.getLocalHost().getName().equalsIgnoreCase(q.getName())
- || this.jmDNSImpl.getServices().containsKey(q.getName().toLowerCase()));
- if (!iAmTheOnlyOne)
- {
- break;
- }
+ logger.finest(this.getName() + "start() question=" + question);
+ }
+ iAmTheOnlyOne = question.iAmTheOnlyOne(this.getDns());
+ if (!iAmTheOnlyOne)
+ {
+ break;
}
}
- int delay = (iAmTheOnlyOne && !in.isTruncated()) ? 0 : DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + JmDNSImpl.getRandom().nextInt(DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - in.elapseSinceArrival();
+ int delay = (iAmTheOnlyOne && !_in.isTruncated()) ? 0 : DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + JmDNSImpl.getRandom().nextInt(DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - _in.elapseSinceArrival();
if (delay < 0)
{
delay = 0;
}
-// logger.finest("start() Responder chosen delay=" + delay);
- this.jmDNSImpl.schedule(this, delay);
+ if (logger.isLoggable(Level.FINEST))
+ {
+ logger.finest(this.getName() + "start() Responder chosen delay=" + delay);
+ }
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled())
+ {
+ timer.schedule(this, delay);
+ }
}
+ @Override
public void run()
{
- synchronized (this.jmDNSImpl.getIoLock())
- {
- if (this.jmDNSImpl.getPlannedAnswer() == in)
- {
- this.jmDNSImpl.setPlannedAnswer(null);
- }
-
- // We use these sets to prevent duplicate records
- // FIXME - This should be moved into DNSOutgoing
- HashSet questions = new HashSet();
- HashSet answers = new HashSet();
+ this.getDns().respondToQuery(_in);
+ // We use these sets to prevent duplicate records
+ Set<DNSQuestion> questions = new HashSet<DNSQuestion>();
+ Set<DNSRecord> answers = new HashSet<DNSRecord>();
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED)
+ if (this.getDns().isAnnounced())
+ {
+ try
{
- try
+ // Answer questions
+ for (DNSQuestion question : _in.getQuestions())
{
- boolean isUnicast = (port != DNSConstants.MDNS_PORT);
-
-
- // Answer questions
- for (Iterator iterator = in.getQuestions().iterator(); iterator.hasNext();)
+ if (logger.isLoggable(Level.FINER))
{
- DNSEntry entry = (DNSEntry) iterator.next();
- if (entry instanceof DNSQuestion)
- {
- DNSQuestion q = (DNSQuestion) entry;
-
- // for unicast responses the question must be included
- if (isUnicast)
- {
- //out.addQuestion(q);
- questions.add(q);
- }
-
- int type = q.getType();
- if (type == DNSConstants.TYPE_ANY || type == DNSConstants.TYPE_SRV)
- { // I ama not sure of why there is a special case here [PJYF Oct 15 2004]
- if (this.jmDNSImpl.getLocalHost().getName().equalsIgnoreCase(q.getName()))
- {
- // type = DNSConstants.TYPE_A;
- DNSRecord answer = this.jmDNSImpl.getLocalHost().getDNS4AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- answer = this.jmDNSImpl.getLocalHost().getDNS6AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- type = DNSConstants.TYPE_IGNORE;
- }
- else
- {
- if (this.jmDNSImpl.getServiceTypes().containsKey(q.getName().toLowerCase()))
- {
- type = DNSConstants.TYPE_PTR;
- }
- }
- }
-
- switch (type)
- {
- case DNSConstants.TYPE_A:
- {
- // Answer a query for a domain name
- //out = addAnswer( in, addr, port, out, host );
- DNSRecord answer = this.jmDNSImpl.getLocalHost().getDNS4AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- break;
- }
- case DNSConstants.TYPE_AAAA:
- {
- // Answer a query for a domain name
- DNSRecord answer = this.jmDNSImpl.getLocalHost().getDNS6AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- break;
- }
- case DNSConstants.TYPE_PTR:
- {
- // Answer a query for services of a given type
-
- // find matching services
- for (Iterator serviceIterator = this.jmDNSImpl.getServices().values().iterator(); serviceIterator.hasNext();)
- {
- ServiceInfoImpl info = (ServiceInfoImpl) serviceIterator.next();
- if (info.getState() == DNSState.ANNOUNCED)
- {
- if (q.getName().equalsIgnoreCase(info.getType()))
- {
- DNSRecord answer = this.jmDNSImpl.getLocalHost().getDNS4AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- answer = this.jmDNSImpl.getLocalHost().getDNS6AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- answers.add(new DNSRecord.Pointer(info.getType(), DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getQualifiedName()));
- answers.add(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL,
- info.getPriority(), info.getWeight(), info.getPort(), this.jmDNSImpl.getLocalHost().getName()));
- answers.add(new DNSRecord.Text(info.getQualifiedName(), DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL,
- info.getText()));
- }
- }
- }
- if (q.getName().equalsIgnoreCase("_services" + DNSConstants.DNS_META_QUERY + "local."))
- {
- for (Iterator serviceTypeIterator = this.jmDNSImpl.getServiceTypes().values().iterator(); serviceTypeIterator.hasNext();)
- {
- answers.add(new DNSRecord.Pointer("_services" + DNSConstants.DNS_META_QUERY + "local.", DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, (String) serviceTypeIterator.next()));
- }
- }
- break;
- }
- case DNSConstants.TYPE_SRV:
- case DNSConstants.TYPE_ANY:
- case DNSConstants.TYPE_TXT:
- {
- ServiceInfoImpl info = (ServiceInfoImpl) this.jmDNSImpl.getServices().get(q.getName().toLowerCase());
- if (info != null && info.getState() == DNSState.ANNOUNCED)
- {
- DNSRecord answer = this.jmDNSImpl.getLocalHost().getDNS4AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- answer = this.jmDNSImpl.getLocalHost().getDNS6AddressRecord();
- if (answer != null)
- {
- answers.add(answer);
- }
- answers.add(new DNSRecord.Pointer(info.getType(), DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getQualifiedName()));
- answers.add(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL,
- info.getPriority(), info.getWeight(), info.getPort(), this.jmDNSImpl.getLocalHost().getName()));
- answers.add(new DNSRecord.Text(info.getQualifiedName(), DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.getText()));
- }
- break;
- }
- default :
- {
- //System.out.println("JmDNSResponder.unhandled query:"+q);
- break;
- }
- }
- }
+ logger.finer(this.getName() + "run() JmDNS responding to: " + question);
}
-
-
- // remove known answers, if the ttl is at least half of
- // the correct value. (See Draft Cheshire chapter 7.1.).
- for (Iterator i = in.getAnswers().iterator(); i.hasNext();)
+ // for unicast responses the question must be included
+ if (_unicast)
{
- DNSRecord knownAnswer = (DNSRecord) i.next();
- if (knownAnswer.getTtl() > DNSConstants.DNS_TTL / 2 && answers.remove(knownAnswer))
- {
-// logger.log(Level.FINER, "JmDNS Responder Known Answer Removed");
- }
+ // out.addQuestion(q);
+ questions.add(question);
}
+ question.addAnswers(this.getDns(), answers);
+ }
- // responde if we have answers
- if (answers.size() != 0)
+ // remove known answers, if the ttl is at least half of the correct value. (See Draft Cheshire chapter 7.1.).
+ long now = System.currentTimeMillis();
+ for (DNSRecord knownAnswer : _in.getAnswers())
+ {
+ if (knownAnswer.isStale(now))
{
-// logger.finer("run() JmDNS responding");
- DNSOutgoing out = null;
- if (isUnicast)
- {
- out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, false);
- }
-
- for (Iterator i = questions.iterator(); i.hasNext();)
- {
- out.addQuestion((DNSQuestion) i.next());
- }
- for (Iterator i = answers.iterator(); i.hasNext();)
+ answers.remove(knownAnswer);
+ if (logger.isLoggable(Level.FINER))
{
- out = this.jmDNSImpl.addAnswer(in, addr, port, out, (DNSRecord) i.next());
+ logger.finer(this.getName() + "JmDNS Responder Known Answer Removed");
}
- this.jmDNSImpl.send(out);
}
- this.cancel();
}
- catch (Throwable e)
+
+ // respond if we have answers
+ if (answers.size() != 0)
{
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.close();
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer(this.getName() + "run() JmDNS responding");
+ }
+ DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA, !_unicast, _in.getSenderUDPPayload());
+ out.setId(_in.getId());
+ for (DNSQuestion question : questions)
+ {
+ out = this.addQuestion(out, question);
+ }
+ for (DNSRecord answer : answers)
+ {
+ out = this.addAnswer(out, _in, answer);
+ }
+ if (!out.isEmpty())
+ this.getDns().send(out);
}
+ // this.cancel();
+ }
+ catch (Throwable e)
+ {
+ logger.log(Level.WARNING, "run() exception ", e);
+ this.getDns().close();
}
}
}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceInfoResolver.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceInfoResolver.java
deleted file mode 100644
index 8252a700d..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceInfoResolver.java
+++ /dev/null
@@ -1,101 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSQuestion;
-import javax.jmdns.impl.DNSRecord;
-import javax.jmdns.impl.DNSState;
-import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
-
-/**
- * The ServiceInfoResolver queries up to three times consecutively for
- * a service info, and then removes itself from the timer.
- * <p/>
- * The ServiceInfoResolver will run only if JmDNS is in state ANNOUNCED.
- * REMIND: Prevent having multiple service resolvers for the same info in the
- * timer queue.
- */
-public class ServiceInfoResolver extends TimerTask
-{
-// static Logger logger = Logger.getLogger(ServiceInfoResolver.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
- /**
- * Counts the number of queries being sent.
- */
- int count = 0;
- private ServiceInfoImpl info;
-
- public ServiceInfoResolver(JmDNSImpl jmDNSImpl, ServiceInfoImpl info)
- {
- this.jmDNSImpl = jmDNSImpl;
- this.info = info;
- info.setDns(this.jmDNSImpl);
- this.jmDNSImpl.addListener(info, new DNSQuestion(info.getQualifiedName(), DNSConstants.TYPE_ANY, DNSConstants.CLASS_IN));
- }
-
- public void start(Timer timer)
- {
- timer.schedule(this, DNSConstants.QUERY_WAIT_INTERVAL, DNSConstants.QUERY_WAIT_INTERVAL);
- }
-
- public void run()
- {
- try
- {
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED)
- {
- if (count++ < 3 && !info.hasData())
- {
- long now = System.currentTimeMillis();
- DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
- out.addQuestion(new DNSQuestion(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN));
- out.addQuestion(new DNSQuestion(info.getQualifiedName(), DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN));
- if (info.getServer() != null)
- {
- out.addQuestion(new DNSQuestion(info.getServer(), DNSConstants.TYPE_A, DNSConstants.CLASS_IN));
- }
- out.addAnswer((DNSRecord) this.jmDNSImpl.getCache().get(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN), now);
- out.addAnswer((DNSRecord) this.jmDNSImpl.getCache().get(info.getQualifiedName(), DNSConstants.TYPE_TXT, DNSConstants.CLASS_IN), now);
- if (info.getServer() != null)
- {
- out.addAnswer((DNSRecord) this.jmDNSImpl.getCache().get(info.getServer(), DNSConstants.TYPE_A, DNSConstants.CLASS_IN), now);
- }
- this.jmDNSImpl.send(out);
- }
- else
- {
- // After three queries, we can quit.
- this.cancel();
- this.jmDNSImpl.removeListener(info);
- }
- }
- else
- {
- if (this.jmDNSImpl.getState() == DNSState.CANCELED)
- {
- this.cancel();
- this.jmDNSImpl.removeListener(info);
- }
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
- }
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceResolver.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceResolver.java
deleted file mode 100644
index 2cc49e8a5..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/ServiceResolver.java
+++ /dev/null
@@ -1,101 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSQuestion;
-import javax.jmdns.impl.DNSRecord;
-import javax.jmdns.impl.DNSState;
-import javax.jmdns.impl.JmDNSImpl;
-import javax.jmdns.impl.ServiceInfoImpl;
-
-/**
- * The ServiceResolver queries three times consecutively for services of
- * a given type, and then removes itself from the timer.
- * <p/>
- * The ServiceResolver will run only if JmDNS is in state ANNOUNCED.
- * REMIND: Prevent having multiple service resolvers for the same type in the
- * timer queue.
- */
-public class ServiceResolver extends TimerTask
-{
-// static Logger logger = Logger.getLogger(ServiceResolver.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
- /**
- * Counts the number of queries being sent.
- */
- int count = 0;
- private String type;
-
- public ServiceResolver(JmDNSImpl jmDNSImpl, String type)
- {
- this.jmDNSImpl = jmDNSImpl;
- this.type = type;
- }
-
- public void start(Timer timer)
- {
- timer.schedule(this, DNSConstants.QUERY_WAIT_INTERVAL, DNSConstants.QUERY_WAIT_INTERVAL);
- }
-
- public void run()
- {
- try
- {
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED)
- {
- if (count++ < 3)
- {
-// logger.finer("run() JmDNS querying service");
- long now = System.currentTimeMillis();
- DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
- out.addQuestion(new DNSQuestion(type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN));
- for (Iterator s = this.jmDNSImpl.getServices().values().iterator(); s.hasNext();)
- {
- final ServiceInfoImpl info = (ServiceInfoImpl) s.next();
- try
- {
- out.addAnswer(new DNSRecord.Pointer(info.getType(), DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getQualifiedName()), now);
- }
- catch (IOException ee)
- {
- break;
- }
- }
- this.jmDNSImpl.send(out);
- }
- else
- {
- // After three queries, we can quit.
- this.cancel();
- }
- }
- else
- {
- if (this.jmDNSImpl.getState() == DNSState.CANCELED)
- {
- this.cancel();
- }
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
- }
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/TypeResolver.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/TypeResolver.java
deleted file mode 100644
index c2bc121f4..000000000
--- a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/TypeResolver.java
+++ /dev/null
@@ -1,92 +0,0 @@
-//Copyright 2003-2005 Arthur van Hoff, Rick Blair
-//Licensed under Apache License version 2.0
-//Original license LGPL
-
-package javax.jmdns.impl.tasks;
-
-import java.util.Iterator;
-import java.util.Timer;
-import java.util.TimerTask;
-//import java.util.logging.Level;
-//import java.util.logging.Logger;
-
-import javax.jmdns.impl.DNSConstants;
-import javax.jmdns.impl.DNSOutgoing;
-import javax.jmdns.impl.DNSQuestion;
-import javax.jmdns.impl.DNSRecord;
-import javax.jmdns.impl.DNSState;
-import javax.jmdns.impl.JmDNSImpl;
-
-/**
- * Helper class to resolve service types.
- * <p/>
- * The TypeResolver queries three times consecutively for service types, and then
- * removes itself from the timer.
- * <p/>
- * The TypeResolver will run only if JmDNS is in state ANNOUNCED.
- */
-public class TypeResolver extends TimerTask
-{
-// static Logger logger = Logger.getLogger(TypeResolver.class.getName());
-
- /**
- *
- */
- private final JmDNSImpl jmDNSImpl;
-
- /**
- * @param jmDNSImpl
- */
- public TypeResolver(JmDNSImpl jmDNSImpl)
- {
- this.jmDNSImpl = jmDNSImpl;
- }
-
- public void start(Timer timer)
- {
- timer.schedule(this, DNSConstants.QUERY_WAIT_INTERVAL, DNSConstants.QUERY_WAIT_INTERVAL);
- }
-
- /**
- * Counts the number of queries that were sent.
- */
- int count = 0;
-
- public void run()
- {
- try
- {
- if (this.jmDNSImpl.getState() == DNSState.ANNOUNCED)
- {
- if (count++ < 3)
- {
-// logger.finer("run() JmDNS querying type");
- DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
- out.addQuestion(new DNSQuestion("_services" + DNSConstants.DNS_META_QUERY + "local.", DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN));
- for (Iterator iterator = this.jmDNSImpl.getServiceTypes().values().iterator(); iterator.hasNext();)
- {
- out.addAnswer(new DNSRecord.Pointer("_services" + DNSConstants.DNS_META_QUERY + "local.", DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, (String) iterator.next()), 0);
- }
- this.jmDNSImpl.send(out);
- }
- else
- {
- // After three queries, we can quit.
- this.cancel();
- }
- }
- else
- {
- if (this.jmDNSImpl.getState() == DNSState.CANCELED)
- {
- this.cancel();
- }
- }
- }
- catch (Throwable e)
- {
-// logger.log(Level.WARNING, "run() exception ", e);
- this.jmDNSImpl.recover();
- }
- }
-} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java
new file mode 100644
index 000000000..75757a4cc
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/DNSResolverTask.java
@@ -0,0 +1,138 @@
+//Licensed under Apache License version 2.0
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.tasks.DNSTask;
+
+/**
+ * This is the root class for all resolver tasks.
+ *
+ * @version %I%, %G%
+ * @author Pierre Frisch
+ */
+public abstract class DNSResolverTask extends DNSTask
+{
+ private static Logger logger = Logger.getLogger(DNSResolverTask.class.getName());
+
+ /**
+ * Counts the number of queries being sent.
+ */
+ protected int _count = 0;
+
+ /**
+ * @param jmDNSImpl
+ */
+ public DNSResolverTask(JmDNSImpl jmDNSImpl)
+ {
+ super(jmDNSImpl);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return super.toString() + " count: " + _count;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer)
+ {
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled())
+ {
+ timer.schedule(this, DNSConstants.QUERY_WAIT_INTERVAL, DNSConstants.QUERY_WAIT_INTERVAL);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.TimerTask#run()
+ */
+ @Override
+ public void run()
+ {
+ try
+ {
+ if (this.getDns().isCanceling() || this.getDns().isCanceled())
+ {
+ this.cancel();
+ }
+ else
+ {
+ if (_count++ < 3)
+ {
+ if (logger.isLoggable(Level.FINER))
+ {
+ logger.finer(this.getName() + ".run() JmDNS " + this.description());
+ }
+ DNSOutgoing out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY);
+ out = this.addQuestions(out);
+ if (this.getDns().isAnnounced())
+ {
+ out = this.addAnswers(out);
+ }
+ if (!out.isEmpty())
+ {
+ this.getDns().send(out);
+ }
+ }
+ else
+ {
+ // After three queries, we can quit.
+ this.cancel();
+ }
+ }
+ }
+ catch (Throwable e)
+ {
+ logger.log(Level.WARNING, this.getName() + ".run() exception ", e);
+ this.getDns().recover();
+ }
+ }
+
+ /**
+ * Overridden by subclasses to add questions to the message.<br/>
+ * <b>Note:</b> Because of message size limitation the returned message may be different than the message parameter.
+ *
+ * @param out
+ * outgoing message
+ * @return the outgoing message.
+ * @throws IOException
+ */
+ protected abstract DNSOutgoing addQuestions(DNSOutgoing out) throws IOException;
+
+ /**
+ * Overridden by subclasses to add questions to the message.<br/>
+ * <b>Note:</b> Because of message size limitation the returned message may be different than the message parameter.
+ *
+ * @param out
+ * outgoing message
+ * @return the outgoing message.
+ * @throws IOException
+ */
+ protected abstract DNSOutgoing addAnswers(DNSOutgoing out) throws IOException;
+
+ /**
+ * Returns a description of the resolver for debugging
+ *
+ * @return resolver description
+ */
+ protected abstract String description();
+
+}
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java
new file mode 100644
index 000000000..79b695b17
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceInfoResolver.java
@@ -0,0 +1,119 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+
+/**
+ * The ServiceInfoResolver queries up to three times consecutively for a service info, and then removes itself from the timer.
+ * <p/>
+ * The ServiceInfoResolver will run only if JmDNS is in state ANNOUNCED. REMIND: Prevent having multiple service resolvers for the same info in the timer queue.
+ */
+public class ServiceInfoResolver extends DNSResolverTask
+{
+
+ private final ServiceInfoImpl _info;
+
+ public ServiceInfoResolver(JmDNSImpl jmDNSImpl, ServiceInfoImpl info)
+ {
+ super(jmDNSImpl);
+ this._info = info;
+ info.setDns(this.getDns());
+ this.getDns().addListener(info, DNSQuestion.newQuestion(info.getQualifiedName(), DNSRecordType.TYPE_ANY, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return "ServiceInfoResolver(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.TimerTask#cancel()
+ */
+ @Override
+ public boolean cancel()
+ {
+ // We should not forget to remove the listener
+ boolean result = super.cancel();
+ if (!_info.isPersistent())
+ {
+ this.getDns().removeListener(_info);
+ }
+ return result;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#addAnswers(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addAnswers(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ if (!_info.hasData())
+ {
+ long now = System.currentTimeMillis();
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getQualifiedName(), DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_IN), now);
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getQualifiedName(), DNSRecordType.TYPE_TXT, DNSRecordClass.CLASS_IN), now);
+ if (_info.getServer().length() > 0)
+ {
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getServer(), DNSRecordType.TYPE_A, DNSRecordClass.CLASS_IN), now);
+ newOut = this.addAnswer(newOut, (DNSRecord) this.getDns().getCache().getDNSEntry(_info.getServer(), DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_IN), now);
+ }
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#addQuestions(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addQuestions(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ if (!_info.hasData())
+ {
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getQualifiedName(), DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getQualifiedName(), DNSRecordType.TYPE_TXT, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ if (_info.getServer().length() > 0)
+ {
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getServer(), DNSRecordType.TYPE_A, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_info.getServer(), DNSRecordType.TYPE_AAAA, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ }
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#description()
+ */
+ @Override
+ protected String description()
+ {
+ return "querying service info: " + (_info != null ? _info.getQualifiedName() : "null");
+ }
+
+} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceResolver.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceResolver.java
new file mode 100644
index 000000000..eea86c514
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/ServiceResolver.java
@@ -0,0 +1,88 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+
+/**
+ * The ServiceResolver queries three times consecutively for services of a given type, and then removes itself from the timer.
+ * <p/>
+ * The ServiceResolver will run only if JmDNS is in state ANNOUNCED. REMIND: Prevent having multiple service resolvers for the same type in the timer queue.
+ */
+public class ServiceResolver extends DNSResolverTask
+{
+
+ private String _type;
+
+ public ServiceResolver(JmDNSImpl jmDNSImpl, String type)
+ {
+ super(jmDNSImpl);
+ this._type = type;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return "ServiceResolver(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#addAnswers(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addAnswers(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ long now = System.currentTimeMillis();
+ for (ServiceInfo info : this.getDns().getServices().values())
+ {
+ newOut = this.addAnswer(newOut, new DNSRecord.Pointer(info.getType(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, info.getQualifiedName()), now);
+ // newOut = this.addAnswer(newOut, new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(),
+ // this.getDns().getLocalHost().getName()), now);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#addQuestions(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addQuestions(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_type, DNSRecordType.TYPE_PTR, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ // newOut = this.addQuestion(newOut, DNSQuestion.newQuestion(_type, DNSRecordType.TYPE_SRV, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#description()
+ */
+ @Override
+ protected String description()
+ {
+ return "querying service";
+ }
+} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/TypeResolver.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/TypeResolver.java
new file mode 100644
index 000000000..3b1ce45f7
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/resolver/TypeResolver.java
@@ -0,0 +1,84 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.tasks.resolver;
+
+import java.io.IOException;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSQuestion;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSRecordType;
+
+/**
+ * Helper class to resolve service types.
+ * <p/>
+ * The TypeResolver queries three times consecutively for service types, and then removes itself from the timer.
+ * <p/>
+ * The TypeResolver will run only if JmDNS is in state ANNOUNCED.
+ */
+public class TypeResolver extends DNSResolverTask
+{
+
+ /**
+ * @param jmDNSImpl
+ */
+ public TypeResolver(JmDNSImpl jmDNSImpl)
+ {
+ super(jmDNSImpl);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return "TypeResolver(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#addAnswers(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addAnswers(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ long now = System.currentTimeMillis();
+ for (String type : this.getDns().getServiceTypes().keySet())
+ {
+ newOut = this.addAnswer(newOut, new DNSRecord.Pointer("_services._dns-sd._udp.local.", DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE, DNSConstants.DNS_TTL, type), now);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#addQuestions(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing addQuestions(DNSOutgoing out) throws IOException
+ {
+ return this.addQuestion(out, DNSQuestion.newQuestion("_services._dns-sd._udp.local.", DNSRecordType.TYPE_PTR, DNSRecordClass.CLASS_IN, DNSRecordClass.NOT_UNIQUE));
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.Resolver#description()
+ */
+ @Override
+ protected String description()
+ {
+ return "querying type";
+ }
+} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Announcer.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Announcer.java
new file mode 100644
index 000000000..5d5acfdc2
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Announcer.java
@@ -0,0 +1,162 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSState;
+
+/**
+ * The Announcer sends an accumulated query of all announces, and advances the state of all serviceInfos, for which it has sent an announce. The Announcer also sends announcements and advances the state of JmDNS itself.
+ * <p/>
+ * When the announcer has run two times, it finishes.
+ */
+public class Announcer extends DNSStateTask
+{
+ static Logger logger = Logger.getLogger(Announcer.class.getName());
+
+ public Announcer(JmDNSImpl jmDNSImpl)
+ {
+ super(jmDNSImpl, defaultTTL());
+
+ this.setTaskState(DNSState.ANNOUNCING_1);
+ this.associate(DNSState.ANNOUNCING_1);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return "Announcer(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return super.toString() + " state: " + this.getTaskState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer)
+ {
+ if (!this.getDns().isCanceling() && !this.getDns().isCanceled())
+ {
+ timer.schedule(this, DNSConstants.ANNOUNCE_WAIT_INTERVAL, DNSConstants.ANNOUNCE_WAIT_INTERVAL);
+ }
+ }
+
+ @Override
+ public boolean cancel()
+ {
+ this.removeAssociation();
+
+ return super.cancel();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#getTaskDescription()
+ */
+ @Override
+ public String getTaskDescription()
+ {
+ return "announcing";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#checkRunCondition()
+ */
+ @Override
+ protected boolean checkRunCondition()
+ {
+ return !this.getDns().isCanceling() && !this.getDns().isCanceled();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForDNS(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : this.getDns().getLocalHost().answers(DNSRecordClass.UNIQUE, this.getTTL()))
+ {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForInfo(javax.jmdns.impl.ServiceInfoImpl, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : info.answers(DNSRecordClass.UNIQUE, this.getTTL(), this.getDns().getLocalHost()))
+ {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#recoverTask(java.lang.Throwable)
+ */
+ @Override
+ protected void recoverTask(Throwable e)
+ {
+ this.getDns().recover();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#advanceTask()
+ */
+ @Override
+ protected void advanceTask()
+ {
+ this.setTaskState(this.getTaskState().advance());
+ if (!this.getTaskState().isAnnouncing())
+ {
+ this.cancel();
+
+ this.getDns().startRenewer();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Canceler.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Canceler.java
new file mode 100644
index 000000000..52ed342ec
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/Canceler.java
@@ -0,0 +1,159 @@
+//Copyright 2003-2005 Arthur van Hoff, Rick Blair
+//Licensed under Apache License version 2.0
+//Original license LGPL
+
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.Timer;
+import java.util.logging.Logger;
+
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSRecord;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSRecordClass;
+import javax.jmdns.impl.constants.DNSState;
+
+/**
+ * The Canceler sends two announces with TTL=0 for the specified services.
+ */
+public class Canceler extends DNSStateTask
+{
+ static Logger logger = Logger.getLogger(Canceler.class.getName());
+
+ public Canceler(JmDNSImpl jmDNSImpl)
+ {
+ super(jmDNSImpl, 0);
+
+ this.setTaskState(DNSState.CANCELING_1);
+ this.associate(DNSState.CANCELING_1);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#getName()
+ */
+ @Override
+ public String getName()
+ {
+ return "Canceler(" + (this.getDns() != null ? this.getDns().getName() : "") + ")";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString()
+ {
+ return super.toString() + " state: " + this.getTaskState();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.DNSTask#start(java.util.Timer)
+ */
+ @Override
+ public void start(Timer timer)
+ {
+ timer.schedule(this, 0, DNSConstants.ANNOUNCE_WAIT_INTERVAL);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.TimerTask#cancel()
+ */
+ @Override
+ public boolean cancel()
+ {
+ this.removeAssociation();
+
+ return super.cancel();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#getTaskDescription()
+ */
+ @Override
+ public String getTaskDescription()
+ {
+ return "canceling";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#checkRunCondition()
+ */
+ @Override
+ protected boolean checkRunCondition()
+ {
+ return true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForDNS(javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForDNS(DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : this.getDns().getLocalHost().answers(DNSRecordClass.NOT_UNIQUE, this.getTTL()))
+ {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#buildOutgoingForInfo(javax.jmdns.impl.ServiceInfoImpl, javax.jmdns.impl.DNSOutgoing)
+ */
+ @Override
+ protected DNSOutgoing buildOutgoingForInfo(ServiceInfoImpl info, DNSOutgoing out) throws IOException
+ {
+ DNSOutgoing newOut = out;
+ for (DNSRecord answer : info.answers(DNSRecordClass.NOT_UNIQUE, this.getTTL(), this.getDns().getLocalHost()))
+ {
+ newOut = this.addAnswer(newOut, null, answer);
+ }
+ return newOut;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#recoverTask(java.lang.Throwable)
+ */
+ @Override
+ protected void recoverTask(Throwable e)
+ {
+ this.getDns().recover();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see javax.jmdns.impl.tasks.state.DNSStateTask#advanceTask()
+ */
+ @Override
+ protected void advanceTask()
+ {
+ this.setTaskState(this.getTaskState().advance());
+ if (!this.getTaskState().isCanceling())
+ {
+ cancel();
+ }
+ }
+} \ No newline at end of file
diff --git a/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/DNSStateTask.java b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/DNSStateTask.java
new file mode 100644
index 000000000..f15be75eb
--- /dev/null
+++ b/providers/bundles/org.eclipse.ecf.provider.jmdns/jmdns/javax/jmdns/impl/tasks/state/DNSStateTask.java
@@ -0,0 +1,217 @@
+//Licensed under Apache License version 2.0
+package javax.jmdns.impl.tasks.state;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.jmdns.ServiceInfo;
+import javax.jmdns.impl.DNSOutgoing;
+import javax.jmdns.impl.DNSStatefulObject;
+import javax.jmdns.impl.JmDNSImpl;
+import javax.jmdns.impl.ServiceInfoImpl;
+import javax.jmdns.impl.constants.DNSConstants;
+import javax.jmdns.impl.constants.DNSState;
+import javax.jmdns.impl.tasks.DNSTask;
+
+/**
+ * This is the root class for all state tasks. These tasks work with objects that implements the {@link javax.jmdns.impl.DNSStatefulObject} interface and therefore participate in the state machine.
+ *
+ * @version %I%, %G%
+ * @author Pierre Frisch
+ */
+public abstract class DNSStateTask extends DNSTask
+{
+ static Logger logger1 = Logger.getLogger(DNSStateTask.class.getName());
+
+ /**
+ * By setting a 0 ttl we effectively expire the record.
+ */
+ private final int _ttl;
+
+ private static int _defaultTTL = DNSConstants.DNS_TTL;
+
+ /**
+ * The state of the task.
+ */
+ private DNSState _taskState = null;
+
+ public abstract String getTaskDescription();
+
+ public static int defaultTTL()
+ {
+ return _defaultTTL;
+ }
+
+ /**
+ * For testing only do not use in production.
+ *
+ * @param value
+ */
+ public static void setDefaultTTL(int value)
+ {
+ _defaultTTL = value;
+ }
+
+ /**
+ * @param jmDNSImpl
+ * @param ttl
+ */
+ public DNSStateTask(JmDNSImpl jmDNSImpl, int ttl)
+ {
+ super(jmDNSImpl);
+ _ttl = ttl;
+ }
+
+ /**
+ * @return the ttl
+ */
+ public int getTTL()
+ {
+ return _ttl;
+ }
+
+ /**
+ * Associate the DNS host and the service info